deepcopy auf subset

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
mael15
User
Beiträge: 9
Registriert: Montag 6. April 2026, 09:26

hallo zusammen!
ich komme von C++ und habe probleme die logik zu verstehen, warum deepcopy(dataset) nicht den gesamten dataset kopiert?

Code: Alles auswählen

print("dataset size: {}".format(len(list(dataset))))    # 2523
eighty = int(len(dataset)*0.8)
twenty = int(len(dataset)) - eighty
train_subset, test_subset = utils.data.random_split(dataset, [eighty, twenty], generator=torch.Generator().manual_seed(1))
print("vor deepcopy: {}".format(len(list(train_subset))))   # 2018
train_subset.dataset = deepcopy(dataset)
print("nach deepcopy: {}".format(len(list(train_subset))))  # 2018 ?!?!?!?
kann mir das jemand erklären und vielleicht zur entsprechenden stelle in der doku verweisen?
danke!
Sirius3
User
Beiträge: 18402
Registriert: Sonntag 21. Oktober 2012, 17:20

deepcop ist selten sinnvoll, was willst Du hier damit bewirken?
Dann ist es nicht gut, irgendwelche Attribute irgendwelcher Instanzen zu überschreiben. Auch hier die Fage: was willst Du damit bewirken?

In diesem Fall erklärt die Doku alles (https://docs.pytorch.org/docs/stable/da ... ata.Subset):
- dataset (Dataset): The whole Dataset
mael15
User
Beiträge: 9
Registriert: Montag 6. April 2026, 09:26

danke für deine antwort!
es soll bewirkt werden, dass spätere änderungen sich nur auf das train_subset auswirken, nicht auf das ursprünglichen dataset.
mich verwirrt, dass nach train_subset.dataset = deepcopy(dataset) das train_subset kleiner ist als das dataset.
das ganze ist teil dieses online kurses: https://ki-kurs.org/app/programming/tut ... ng/id=3_05
Sirius3
User
Beiträge: 18402
Registriert: Sonntag 21. Oktober 2012, 17:20

Auch wenn das in dem Tutorial funktionieren mag, macht man es nicht.
Es gibt einen Weg, train_subset zu erzeugen, wenn Du veränderte Datensätze hast, dann erzeuge einen neuen Subset.
Statt Daten zu verändern erzeugt man in Python am besten einfach eine neue Datenstruktur.
mael15
User
Beiträge: 9
Registriert: Montag 6. April 2026, 09:26

okay, danke dir, aber kannst du mein problem nachvollziehen und erklären warum sich python so verhält? auch wenn es nicht der beste weg ist möchte ich das ergebnis gerne verstehen.
Sirius3
User
Beiträge: 18402
Registriert: Sonntag 21. Oktober 2012, 17:20

Was verstehst Du daran nicht? Du ersetzt den Dataset durch eine Kopie. Warum sollte sich was ändern?
Benutzeravatar
__blackjack__
User
Beiträge: 14378
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mael15: Also ich kann Dein Problem nicht nachvollziehen. Das Verhalten ist IMHO gar nicht überraschend und das wäre in C++ genau so, das ist also keine Besonderheit von Python.

Du änderst ja letztendlich gar nichts wenn Du `train_subset.dataset` durch eine Kopie ersetzt. Das war vorher das gesamte Dataset und ist danach halt eine Kopie des gesamten Dataset. Also weder die Grösse noch der Wert hat sich verändert, warum sollte das eine Auswirkung auf die Grösse von `train_subset` haben? Das wäre überraschend wenn dem so wäre. Die Grösse von `train_subset.dataset` stellt ja nur die obere Grössenbegrenzung dar. Ist ja auch logisch weil eine Teilmenge nicht grösser sein kann als die Grundmenge aus der sie genommen wurde. Die Grösse der Teilmenge ergibt sich aus der Menge der Indizes die beschreiben welche Elemente aus der Grundmenge zur Teilmenge gehören.

Dir ist klar, dass nach dem `random_split()` gilt ``dataset == train_subset.dataset``? Die sind gleich gross und haben den gleichen Inhalt. Und sind ziemlich wahrscheinlich auch das _selbe_ Objekt. Sonst würde das ersetzen durch eine Kopie ja noch weniger Sinn machen.
Who is General Failure and why is he reading my hard disk?
mael15
User
Beiträge: 9
Registriert: Montag 6. April 2026, 09:26

__blackjack__ hat geschrieben: Mittwoch 8. April 2026, 14:41Dir ist klar, dass nach dem `random_split()` gilt ``dataset == train_subset.dataset``?
das wäre eine erklärung, das scheint aber nicht der fall zu sein. ich habe die ergebnisse von print jeweils dahinter in den kommentar geschrieben:

Code: Alles auswählen

print("dataset size vor split: {}".format(len(list(dataset))))      # 2523
eighty = int(len(dataset)*0.8)
twenty = int(len(dataset)) - eighty
train_subset, test_subset = utils.data.random_split(dataset, [eighty, twenty], generator=torch.Generator().manual_seed(1))
print("dataset size nach split: {}".format(len(list(dataset))))     # 2523
print("train_subset vor deepcopy: {}".format(len(list(train_subset))))     # 2018
train_subset.dataset = deepcopy(dataset)
print("train_subset nach deepcopy: {}".format(len(list(train_subset))))  # 2018 ?!?!?!? ich erwarte 2523 für den gesamten dataset
mael15
User
Beiträge: 9
Registriert: Montag 6. April 2026, 09:26

Sirius3 hat geschrieben: Mittwoch 8. April 2026, 14:39 Was verstehst Du daran nicht? Du ersetzt den Dataset durch eine Kopie. Warum sollte sich was ändern?
ist vielleicht die lösung, dass train_subset.dataset nicht etwa ein eigenes dataset von train_subset ist, sondern train_subset.dataset heißt: "es ist ein subset vom ursprünglichen und unveränderten dataset?
diese notation mit punkt train_subset.dataset würde in c++ bedeuten: dataset ist ein member von train_subset und somit NICHT das ursprüngliche dataset. ist das vielleicht in python anders?

EDIT: japp, das scheint die erklärung zu sein. sehr andere syntax logik in python verglichen mit c++.
plus zusatzverwirrung, dass der datensatz in der variable "dataset" gespeichert war und dann auf das attribut "dataset" des subsets zugegriffen wird.
Danke!
Sirius3
User
Beiträge: 18402
Registriert: Sonntag 21. Oktober 2012, 17:20

Nein, das ist ziemlich das exakt gleiche Verhalten, was ich auch bei C++ so erwarten würde, wenn ich mit Referenzen arbeite: die Variable dataset und das Attribut train_subset.dataset verweisen ürsprünglich auf das selbe Objekt.
Und ein Subset hat noch zusätzlich ein Array mit den Indizes ins komplette Dataset.
mael15
User
Beiträge: 9
Registriert: Montag 6. April 2026, 09:26

zusätzlich verwirrend war in dem beispiel für mich, dass es in python dynamische variablen für objekte gibt. ich muss einiges anders lesen als ich es seit jahren gewohnt bin. deshalb konnte ich umgekehrt auch meine frage nicht gut formulieren.
aber mein problem ist gelöst durch eure fragen, danke.
Benutzeravatar
snafu
User
Beiträge: 6963
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Stell dir aus C++ Sicht einfach vor, dass vor jedem ``name = wert`` ein ``auto`` steht, vielleicht wird es dann klarer.
Benutzeravatar
__blackjack__
User
Beiträge: 14378
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mael15: In dem Kommentar mit den `?!?!?!?` schreibst Du, Du erwartest 2523 für das gesamte Dataset. Das stimmt ja auch. Die Zahl die dort steht ist aber die Anzahl im Subset. Warum hast Du an der Stelle geglaubt, dass ``len(list(train_subset)))`` die Anzahl des gesamten Datasets liefert? Du fragst doch da das _Subset_ ab‽ Wenn da `dataset` oder `train_subset.dataset` stehen würde, dann bekommst Du auch die Anzahl der Elemente vom gesamten Datensatz.

Die Punktnotation bedeutet doch eigentlich was Du erwartest: ”member von” (in Python-Sprech ”Attribut von”). Und wenn man einem Member/Attribut etwas zuweist das einfach nur eine 1:1 Kopie von dem ist was da vorher zugewiesen war, dann erwartet man in der Regel nicht, dass sich Eigenschaften vom Objekt ändern, deren Member/Attribut man im Grunde nichts wirklich anderes zugewiesen hat.

Zumindest von `Subset`-Objekten kann man übrigens direkt die Länge mit `len` abfragen, ohne vorher eine Liste mit allen Elementen zu erstellen um dann von _der_ die Länge abzufragen. Wo das ursprüngliche `dataset` her kommt, und welchen konkreten Typ das hat, sieht man am gezeigten Code ja nicht. Muss aber mindestens ein `IterableDataset` sein, denn mit einem reinen `Dataset` funktioniert das ``len(list(dataset))`` nicht. Vielleicht ginge da auch schon direkt ``len(dataset)``, ohne die temporäre Liste.
Who is General Failure and why is he reading my hard disk?
mael15
User
Beiträge: 9
Registriert: Montag 6. April 2026, 09:26

__blackjack__ hat geschrieben: Donnerstag 9. April 2026, 10:52 @mael15: In dem Kommentar mit den `?!?!?!?` schreibst Du, Du erwartest 2523 für das gesamte Dataset. Das stimmt ja auch. Die Zahl die dort steht ist aber die Anzahl im Subset. Warum hast Du an der Stelle geglaubt, dass ``len(list(train_subset)))`` die Anzahl des gesamten Datasets liefert? Du fragst doch da das _Subset_ ab‽ Wenn da `dataset` oder `train_subset.dataset` stehen würde, dann bekommst Du auch die Anzahl der Elemente vom gesamten Datensatz.

Die Punktnotation bedeutet doch eigentlich was Du erwartest: ”member von” (in Python-Sprech ”Attribut von”). Und wenn man einem Member/Attribut etwas zuweist das einfach nur eine 1:1 Kopie von dem ist was da vorher zugewiesen war, dann erwartet man in der Regel nicht, dass sich Eigenschaften vom Objekt ändern, deren Member/Attribut man im Grunde nichts wirklich anderes zugewiesen hat.

Zumindest von `Subset`-Objekten kann man übrigens direkt die Länge mit `len` abfragen, ohne vorher eine Liste mit allen Elementen zu erstellen um dann von _der_ die Länge abzufragen. Wo das ursprüngliche `dataset` her kommt, und welchen konkreten Typ das hat, sieht man am gezeigten Code ja nicht. Muss aber mindestens ein `IterableDataset` sein, denn mit einem reinen `Dataset` funktioniert das ``len(list(dataset))`` nicht. Vielleicht ginge da auch schon direkt ``len(dataset)``, ohne die temporäre Liste.
Vielen Dank! Wie bereits vorher geschrieben habe ich es mittlerweile verstanden. :) Leider scheint es in diesem Forum keine Funktion zu geben, mit der man das Thema als erledigt markieren kann.
Antworten