Heim >Backend-Entwicklung >Python-Tutorial >Wie mich eine Lotteriequest zu den Kräften von PyTorch führte
Irgendwann haben Sie wahrscheinlich gehört, dass Ihre Gewinnchancen bei einer Lotterie sehr gering sind. Wie bei allen Dingen, die mit Wahrscheinlichkeit zu tun haben, können mehrere Versuche das Ergebnis zu Ihren Gunsten beeinflussen. Wenn Sie nun an vielen Lotterien teilnehmen würden, wären Ihre Gewinnchancen bei einer Lotterie etwas besser, je nachdem, an wie vielen weiteren Lotterien Sie teilgenommen haben. Dies ist noch keineswegs eine Garantie dafür, dass Sie irgendwann gewinnen, sondern bei gleichmäßiger Verteilung , und nach dem Gesetz der großen Zahlen (in diesem Fall bedeutet das eine große Anzahl von Lotterien) können wir zu relativ wahrscheinlicheren Möglichkeiten gelangen.
Es ist wichtig zu verstehen, dass jede neue Lotterie unabhängig von jeder anderen ist und dass dieselbe Lotterie-„Losnummer“ viele verschiedene Lotterien gewinnen kann (gemäß dem Gesetz der großen Zahlen). Sie könnten auch Pech haben und in jeder Lotterie die falsche Zahl ziehen, egal wie oft Sie es versucht haben. Sie haben jetzt zwei Möglichkeiten:
Theoretisch (und mathematisch) haben beide Szenarien die gleiche Eintrittswahrscheinlichkeit. Szenario 2 verschafft Ihnen jedoch einen leichten Vorteil. Wenn sich die Häufigkeit der Unendlichkeit nähert, wird schließlich jede Zahl ausgewählt. Das Problem besteht darin, dass Sie es bei Szenario 1 mehrmals versuchen müssen, in der Hoffnung, dass die Zahl, die Sie zu diesem Zeitpunkt ausgewählt haben, mit der Zahl übereinstimmt, die gewinnt. Bei Szenario 2 sind Sie sicher, dass Ihre Zahl irgendwann „gewinnen“ wird, da die Versuche gegen Unendlichkeit tendieren. Für diesen Blogbeitrag verwenden wir Szenario 2.
Glauben Sie also, dass Sie diese Frage beantworten können, bevor ich Ihnen die Antwort verrate?
„Wenn alle Lotterien um Sie herum Slots für genau 1 Million Menschen hätten und Sie für alle, die Sie gespielt haben, das gleiche Los [x] ausgewählt hätten, wie viele Lotterien müssten Sie dann spielen, um endlich ein Gewinner zu sein?“ (Kommentieren Sie gerne Ihre erste Antwort)
Die Antwort ist...
Etwa 14,4 Millionen Mal.
Im Rest dieses Blogbeitrags geht es darum, wie ich zu diesem Wert gekommen bin, wie die Simulationen durchgeführt wurden und einige Vorbehalte. Ab jetzt wird es technischer.
Die Loszahlen einer Lotterie mit 1 Million Teilnehmern würden zwischen 1 und 1.000.000 (oder 0 und 999.999) liegen. Spieler können für jede Lotterie nur eine Zahl innerhalb dieses Bereichs auswählen und der Gewinnschein kann nur aus diesem Bereich stammen. Im Wesentlichen können wir sagen, dass wir einen Satz von 1 Million Zahlen haben werden.
Unter Berücksichtigung der Tatsache, dass ein Benutzer eine beliebige Zahl innerhalb dieses Bereichs auswählen kann, müssen wir die Bedingung erfüllen, dass jedes Element im Set mindestens einmal getroffen wird. Dies liegt daran, dass, wenn jede Nummer mindestens einmal aufgerufen wurde, alle möglichen Ticketnummern abgedeckt würden, die ein Spieler hätte auswählen können. Das bedeutet auch, dass es uns egal ist, wie oft jede Zahl ausgeführt wird, was eine „Menge“ zur idealen Python-Datenstruktur für unsere Simulation macht. Wir beginnen mit einer leeren Menge und füllen sie bei jeder Iteration mit einer zufällig generierten Zahl, bis die Menge alle Zahlen innerhalb des angegebenen Bereichs enthält. Da Python-Sets keine Zahlen wiederholen, müssen wir uns keine Sorgen um die Sicherstellung der Eindeutigkeit machen.
def calculate_lottery_chances(lottery_players_count): number_set = set() count = 0 while len(number_set) < lottery_players_count: gen_number = random.randint(1, lottery_players_count) number_set.add(gen_number) count += 1 return count
Für eine Lotterie mit 1.000.000 Personen würde der Funktionsaufruf wie folgt aussehen: berechne_lottery_chances(1000000) und würde die Anzahl der Lotterieversuche vor dem Gewinn zurückgeben. Durch die Anordnung des Codes auf diese Weise ist er sehr erweiterbar.
Kurz gesagt, die Hauptursache des Problems ist „Variation“. Als ich die Funktion zum ersten Mal ausführte, erhielt ich den Wert „13,1 Millionen“. Ich habe es noch einmal durchgelesen und einen Wert in der Größenordnung von 13,9 Millionen erhalten. Ich habe das noch öfter gemacht und sehr unterschiedliche Antworten bekommen – irgendwann kam ich auf 15 Millionen. Es war klar, dass ich dies tun und einen Durchschnitt ermitteln musste. Dem bisher bestehenden Muster folgend, kam ich zu dem Schluss, dass ich, da die Anzahl der Iterationen zur Mittelung gegen Unendlich tendiert, näher dran wäre, eine zuverlässige Antwort zu haben. Es bestand Bedarf an etwas, das dies tun konnte, und zwar schnell, und das veranlasste mich, diese Funktion zu schreiben:
def average_over_n_times(function, function_arg, n): """ This returns the average of the returned value of a function when it is called n times, with its (one) arg """ total = 0 for x in range(0, n): total += function(function_arg) return round(total/n)
Anschließend würde dann alles wie folgt geflickt:
num_of_trials = average_over_n_times(calculate_lottery_chances, lottery_players_count, n)
Wobei „n“ die Häufigkeit darstellt, mit der die Ergebnisse gemittelt werden. Dies wirft jedoch ein weiteres Problem auf, das im nächsten Abschnitt besprochen wird.
Je größer der Wert von n, desto näher kommt man einem „Durchschnittsfall“-Ergebnis. Wenn man jedoch bedenkt, dass es immer noch keine Absolutheiten oder Gewissheiten gibt, ist es nicht mehr produktiv, diese Reihe von Aufgaben zu oft auszuführen. Ich sage das aus folgenden Gründen:
Vor diesem Hintergrund habe ich „n“ mit den Werten 10, 20, 30, 50, 100, 1000 und 5000 Mal getestet.
An dieser Stelle fragen Sie sich wahrscheinlich, warum das Wort „PyTorch“ im Titel des Blogbeitrags noch nicht einmal erwähnt wurde. Nun, obwohl ich das Testen von n mit unterschiedlichen Werten erwähnt habe, war es nicht derselbe Code, den ich für alle Tests verwendet habe.
Das waren rechenintensive Experimente und meine CPU hatte ein Wort mit mir. Die Codeausschnitte, die ich zuvor geteilt habe, wurden in einer Datei geschrieben, die keinerlei externe Paketabhängigkeiten aufwies, und die Datei wurde in der Bash-Shell mit dem vorangestellten Befehl „time“ ausgeführt, um die Ausführungszeiten zu verfolgen. So sahen die Ausführungszeiten aus, wenn nur die CPU verwendet wurde:
n | Time (min and sec) |
---|---|
10 | 1m34.494s |
20 | 3m2.591s |
30 | 5m19.903s |
50 | 10m58.844s |
100 | 14m56.157s |
Bei 1000 konnte ich das Programm nicht mehr zum Laufen bringen. Ich war mir nicht sicher, ob es auf halbem Weg kaputt ging und die Ausführung nicht gestoppt werden konnte, aber ich brach es nach 4 Stunden und 57 Minuten ab. Meiner Meinung nach gibt es einige Faktoren, die dies beeinflusst haben und die ich im Abschnitt „Vorbehalte“ besprechen werde. Wie dem auch sei, das Geräusch meines Lüfters war dröhnend und ich wusste, dass ich die bescheidene CPU meines Laptops möglicherweise etwas zu stark beansprucht hatte. Ich weigerte mich, eine Niederlage hinzunehmen, und während ich darüber nachdachte, was ich tun könnte, um zumindest vierstellige Iterationen durchzuführen, fiel mir etwas ein, was mir ein Freund, der mit PyTorch arbeitete, sagte:
„GPUs sind im Allgemeinen effizienter und rechenintensiver als CPUs“
PyTorch nutzt die GPU und ist damit das perfekte Werkzeug für diese Aufgabe.
PyTorch würde für Berechnungen für unsere Zwecke verwendet werden, daher würde eine Umgestaltung des vorhandenen berechne_lottery_chances()-Codes eine Änderung CPU-abhängiger numerischer Operationen und den Wechsel zu geeigneten PyTorch-Datenstrukturen bedeuten. Kurz und knapp:
Der Refactor von berechne_lottery_chances würde wie folgt aussehen:
def calculate_lottery_chances(lottery_players_count): number_set = set() count = 0 while len(number_set) < lottery_players_count: gen_number = random.randint(1, lottery_players_count) number_set.add(gen_number) count += 1 return count
Ich habe mein Gerät auf „xpu“ eingestellt, weil mein Computer eine Intel-Grafik-GPU verwendet, die PyTorch unterstützt.
Um sicherzustellen, dass meine GPU während der Ausführung verwendet wurde, habe ich meinen Windows-Task-Manager geöffnet und vor der Ausführung zum Abschnitt „Leistung“ navigiert. Beim Laufen bemerkte ich einen spürbaren Anstieg der GPU-Ressourcennutzung.
Zum Kontext hier ein Vorher vs. Nachher:
Vorher:
Beachten Sie, dass die GPU-Auslastung bei 1 % liegt
Nachher:
Beachten Sie, dass die GPU-Auslastung bei 49 % liegt
Bei den Laufzeiten für unterschiedliche Werte von n war die GPU um ein Vielfaches schneller. Es ließ Werte von n unter 100 konstant in weniger als einer Minute laufen und konnte einen Wert von n bei 5000 (fünftausend!)
berechnenHier ist eine Tabelle der Laufzeiten mit der GPU:
n | Time (min and sec) |
---|---|
10 | 0m13.920s |
20 | 0m18.797s |
30 | 0m24.749s |
50 | 0m34.076s |
100 | 1m12.726s |
1000 | 16m9.831s |
Um einen visuellen Eindruck davon zu bekommen, wie groß die Leistungslücke zwischen GPU- und CPU-Vorgängen für dieses Experiment war, finden Sie hier eine Datenvisualisierung, über die Sie nachdenken sollten:
Die x-Achse wurde auf 100 begrenzt, da ich von der CPU keine realistisch „aktuelle“ Ausgabe mehr erhalten konnte und somit kein Raum für einen Vergleich mit der GPU blieb. Die Durchführung der Experimente mit Zahlen im Bereich von 1000 bis 5000 ergab in den meisten Fällen etwa „14,4 Millionen Mal“. So habe ich die Antwort von vorhin bekommen.
Dieses Experiment ging von Annahmen aus und stützte sich auf bestimmte Vorgehensweisen. Darüber hinaus bedeutet meine Unerfahrenheit mit PyTorch möglicherweise, dass es möglicherweise einen effizienteren Ansatz gegeben hat. Hier sind einige zu berücksichtigende Faktoren, die möglicherweise entweder die Genauigkeit meiner Ergebnisse oder die Ausführungszeiten beeinflusst haben:
Abschließend möchte ich darauf hinweisen, dass ich PyTorch zum ersten Mal für irgendetwas verwendet habe und von der Leistung ziemlich beeindruckt war.
Als ich damit in den Kaninchenbau ging, hatte ich nicht damit gerechnet, solche Leistungssteigerungen zu sehen. Ich habe die Idee hinter Tensoren und ein paar Dinge über die unterstützenden Mechanismen hinter noch rechenintensiveren Aufgaben gelernt. Sie haben die Freiheit, die Codeschnipsel nach Belieben zu verwenden, zu replizieren oder zu ändern.
Vielen Dank, dass Sie mich verwöhnt haben, und ich hoffe, Sie hatten eine unterhaltsame Lektüre.
Bis zum nächsten Mal,
Prost. ?
Das obige ist der detaillierte Inhalt vonWie mich eine Lotteriequest zu den Kräften von PyTorch führte. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!