Ich lese ein Meisterwerk, ** "Deep Learning from Zero 2" **. Diesmal ist ein Memo von Kapitel 4. Um den Code auszuführen, laden Sie den gesamten Code von Github herunter und verwenden Sie jupyter notebook in ch04.
In Kapitel 4 geht es darum, das in Kapitel 3 implementierte Word2vec CBOW-Modell zu beschleunigen und zu einem praktischen Modell zu machen. Führen Sie ch04 / train.py aus und sehen Sie sich den Inhalt der Reihe nach an.
Der Datensatz verwendet Penn Tree Bank, die Anzahl der Vokabeln beträgt 10.000 und die Korpusgröße des Zuges. Ist etwa 900.000 Wörter.
import sys
sys.path.append('..')
from common import config
#Wenn Sie mit einer GPU arbeiten, löschen Sie den folgenden Kommentar (Cupy erforderlich).
# ===============================================
# config.GPU = True
# ===============================================
from common.np import *
import pickle
from common.trainer import Trainer
from common.optimizer import Adam
from cbow import CBOW
from skip_gram import SkipGram
from common.util import create_contexts_target, to_cpu, to_gpu
from dataset import ptb
#Hyper-Parametereinstellungen
window_size = 5
hidden_size = 100
batch_size = 100
max_epoch = 10
#Daten lesen
corpus, word_to_id, id_to_word = ptb.load_data('train')
vocab_size = len(word_to_id)
#Holen Sie sich Kontext und Ziel
contexts, target = create_contexts_target(corpus, window_size)
if config.GPU:
contexts, target = to_gpu(contexts), to_gpu(target)
#Netzwerkaufbau
model = CBOW(vocab_size, hidden_size, window_size, corpus)
#Lernen, Verlustübergangsdiagrammanzeige
optimizer = Adam()
trainer = Trainer(model, optimizer)
trainer.fit(contexts, target, max_epoch, batch_size)
trainer.plot()
#Speichern Sie die Daten, die Sie zur späteren Verwendung benötigen
word_vecs = model.word_vecs
if config.GPU:
word_vecs = to_cpu(word_vecs)
params = {}
params['word_vecs'] = word_vecs.astype(np.float16)
params['word_to_id'] = word_to_id
params['id_to_word'] = id_to_word
pkl_file = 'cbow_params.pkl' # or 'skipgram_params.pkl'
with open(pkl_file, 'wb') as f:
pickle.dump(params, f, -1)
Der Verlust scheint stetig gesunken zu sein. Dann wird es ein Punkt. Werfen wir einen Blick auf class CBOW
in cbow.py
im Netzwerkaufbauteil.
# --------------- from cbow.py ---------------
class CBOW:
def __init__(self, vocab_size, hidden_size, window_size, corpus):
V, H = vocab_size, hidden_size
#Gewichtsinitialisierung
W_in = 0.01 * np.random.randn(V, H).astype('f')
W_out = 0.01 * np.random.randn(V, H).astype('f')
#Schichterzeugung
self.in_layers = []
for i in range(2 * window_size):
layer = Embedding(W_in) #Verwenden Sie die Einbettungsebene
self.in_layers.append(layer)
self.ns_loss = NegativeSamplingLoss(W_out, corpus, power=0.75, sample_size=5)
#Listen Sie alle Gewichte und Verläufe auf
layers = self.in_layers + [self.ns_loss]
self.params, self.grads = [], []
for layer in layers:
self.params += layer.params
self.grads += layer.grads
#Legen Sie die verteilte Darstellung von Wörtern in Mitgliedsvariablen fest
self.word_vecs = W_in
Einer der Punkte der Beschleunigung ist die Einführung der ** Einbettungsschicht **. Schauen Sie sich common / layer.py an.
# --------------- from common/layers.py --------------
class Embedding:
def __init__(self, W):
self.params = [W]
self.grads = [np.zeros_like(W)]
self.idx = None
def forward(self, idx):
W, = self.params
self.idx = idx
out = W[idx] #Geben Sie die durch idx angegebene Zeile aus
return out
def backward(self, dout):
dW, = self.grads
dW[...] = 0
if GPU:
np.scatter_add(dW, self.idx, dout)
else:
np.add.at(dW, self.idx, dout) #Fügen Sie der von idx angegebenen Zeile Daten hinzu
return None
In Kapitel 3 wurde die ** MatMul-Ebene ** verwendet, um das innere Produkt des Vektors und der Gewichtsmatrix zu finden. Wenn Sie jedoch darüber nachdenken, ist es das innere Produkt des One-Hot-Vektors und der Gewichtsmatrix, also ** die Gewichtsmatrix $ W_ {in} Sie müssen lediglich die $ line ** angeben. Dies ist die ** Ebene einbetten **.
Auf diese Weise muss die Backpropagation nur die entsprechende Zeile mit den zuvor übertragenen Daten aktualisieren. Beim Mini-Batch-Lernen ist es jedoch möglich, dass mehrere Daten in dieselbe Zeile zurückkehren und sich überlappen. Statt zu ersetzen, werden ** Daten hinzugefügt **.
4.Negative Sampling Der zweite Punkt der Beschleunigung ist ** Negative Sampling **. Wie in Kapitel 3 ist es unrealistisch, nach Softmax anhand der Ausgabe der Anzahl der Vokabeln zu klassifizieren. Was sollen wir dann tun? Die Antwort lautet **, um das mehrwertige Klassifizierungsproblem zu lösen, indem es an das binäre Klassifizierungsproblem angenähert wird **.
Schauen Sie sich class NegativeSamplingLoss
in negative_sampling_layer.py
an.
# ------------- form negative_sampling_layer.py --------------
class NegativeSamplingLoss:
def __init__(self, W, corpus, power=0.75, sample_size=5):
self.sample_size = sample_size
self.sampler = UnigramSampler(corpus, power, sample_size)
self.loss_layers = [SigmoidWithLoss() for _ in range(sample_size + 1)]
self.embed_dot_layers = [EmbeddingDot(W) for _ in range(sample_size + 1)]
self.params, self.grads = [], []
for layer in self.embed_dot_layers:
self.params += layer.params
self.grads += layer.grads
def forward(self, h, target):
batch_size = target.shape[0]
negative_sample = self.sampler.get_negative_sample(target)
#Positives Beispiel vorwärts
score = self.embed_dot_layers[0].forward(h, target)
correct_label = np.ones(batch_size, dtype=np.int32)
loss = self.loss_layers[0].forward(score, correct_label)
#Negativ vorwärts
negative_label = np.zeros(batch_size, dtype=np.int32)
for i in range(self.sample_size):
negative_target = negative_sample[:, i]
score = self.embed_dot_layers[1 + i].forward(h, negative_target)
loss += self.loss_layers[1 + i].forward(score, negative_label)
return loss
def backward(self, dout=1):
dh = 0
for l0, l1 in zip(self.loss_layers, self.embed_dot_layers):
dscore = l0.backward(dout)
dh += l1.backward(dscore)
return dh
Um die mehrwertige Klassifizierung an die binäre Klassifizierung anzunähern, stellen Sie zunächst die Wahrscheinlichkeit ein, dass say (1) für die Antwort des Wortes zwischen Ihnen (0) und auf Wiedersehen (2) so weit wie möglich korrekt ist (korrekt). Beispiel). Das reicht aber nicht.
Daher füge ich hinzu, dass die Wahrscheinlichkeit, dass ein richtig ausgewähltes Hallo (5) oder I (4) falsch ist, so groß wie möglich ist (negatives Beispiel).
Diese Technik wird als ** Negative Sampling ** bezeichnet. Die Anzahl der negativen Beispiele zur Auswahl ist sample_size = 5 im Code.
An dieser Stelle wird "Embedding_dot_layers" angezeigt. Sehen Sie sich dies ebenfalls an. Ebenso ist es in negative_sampling_layer.py
.
# ------------- form negative_sampling_layer.py --------------
class EmbeddingDot:
def __init__(self, W):
self.embed = Embedding(W)
self.params = self.embed.params
self.grads = self.embed.grads
self.cache = None
def forward(self, h, idx):
target_W = self.embed.forward(idx)
out = np.sum(target_W * h, axis=1)
self.cache = (h, target_W)
return out
def backward(self, dout):
h, target_W = self.cache
dout = dout.reshape(dout.shape[0], 1)
dtarget_W = dout * h
self.embed.backward(dtarget_W)
dh = dout * target_W
return dh
Um Mini-Batch zu unterstützen, wird am Ende die Summe von target_w * h genommen, damit sie auch dann berechnet werden kann, wenn mehrere idx und h vorhanden sind.
Zuerst haben wir ch04 / train.py verschoben, damit die gelernten Parameter in cbow_params.pkl
gespeichert werden. Verwenden Sie diese Option, um mit eval.py
zu überprüfen, ob die verteilte Darstellung von Wörtern gut ist.
import sys
sys.path.append('..')
from common.util import most_similar, analogy
import pickle
pkl_file = 'cbow_params.pkl' #Angabe des Dateinamens
#Lesen Sie jeden Parameter
with open(pkl_file, 'rb') as f:
params = pickle.load(f)
word_vecs = params['word_vecs']
word_to_id = params['word_to_id']
id_to_word = params['id_to_word']
# most similar task
querys = ['you']
for query in querys:
most_similar(query, word_to_id, id_to_word, word_vecs, top=5)
Überprüfen Sie zunächst die Wortähnlichkeit mit der Methode "most_similar" (common / util.py). Das, was Ihnen am nächsten kommt, sind wir und ich, sie, Ihre, gefolgt von persönlichen Synonymen. Dies ist das Ergebnis der Berechnung der Ähnlichkeit jedes Wortes mit der folgenden Kosinusähnlichkeit.
# analogy task
analogy('king', 'man', 'queen', word_to_id, id_to_word, word_vecs)
Lassen Sie uns nun das berühmte Problem ** König - Mann + Frau = Königin ** mit der Alalogie-Methode (common / util.py) überprüfen. Das stimmt, nicht wahr?
Dies löst die Aufgabe, das ** Wort x ** so zu finden, dass der ** "König → x" -Vektor ** dem ** "Mann → Frau" -Vektor ** so nahe wie möglich kommt.
Recommended Posts