Ich lese ** "Developmental Deep Learning mit PyTorch" **. Dieses Mal habe ich in Kapitel 7 Transformer studiert, daher möchte ich meine eigene Zusammenfassung ausgeben.
Im Jahr 2017 wurde ein epochales Papier ** "Attention All You Need" ** im Bereich der Verarbeitung natürlicher Sprache veröffentlicht. Das vorgeschlagene Modell war ** Transformer **, das SoTA allein mit ** Attention ** erreichte, ohne eines der zuvor gängigen RNNs für Übersetzungsaufgaben zu verwenden.
Seitdem haben Modelle, die auf diesem Transformer basieren, wie ** BERT, XLNet und ALBERT **, das Gebiet der Verarbeitung natürlicher Sprache dominiert, und es wurde Transformer für die Verarbeitung natürlicher Sprache genannt.
Hier ist ein Modelldiagramm des Transformators, der die Übersetzungsaufgabe ausführt. Wenn Sie beispielsweise die japanisch-englische Übersetzung berücksichtigen, lernt der ** Encoder ** auf der linken Seite die Aufmerksamkeit jedes Wortes im japanischen Satz, und der ** Decoder ** auf der rechten Seite lernt die Aufmerksamkeit jedes Wortes im englischen Satz, während er sich auf diese Informationen bezieht. ist. Lassen Sie mich nun fünf Funktionen erläutern.
1) Psitional Encoding Das größte Ziel von Transformer ist es, die GPU zu verwenden und die Verarbeitungsgeschwindigkeit erheblich zu erhöhen, indem alle Wörter ** parallel ** für jeden Satz verarbeitet werden, anstatt die Wörter einzeln wie RNN zu verarbeiten. .. Daher fügt ** Positionscodierung ** jedem Wort Wortordnungsinformationen hinzu, um zu verhindern, dass die Wortreihenfolgeinformationen aufgrund der Parallelverarbeitung verloren gehen.
2) Scaled dot-product Attention Dies ist das Herz von Transformer, daher werde ich es etwas genauer erklären. Für die Aufmerksamkeitsberechnung ** Abfrage ** (Wortvektor, für den die Aufmerksamkeit berechnet wird), ** Schlüssel ** (Sammlung von Wortvektoren, die für die Relevanzberechnung verwendet werden), ** Wert ** (Vektor für die Berechnung der gewichteten Summe) 3) erscheint.
Ich werde erklären, wie man die Aufmerksamkeit von "I" berechnet, wenn der Satz aus 5 Wörtern besteht: "I", "Ha", "Cat", "De" und "Aru".
Da der Relevanzgrad durch das innere Produkt der Vektoren berechnet werden kann, wird das innere Produkt des "I" -Vektors ** Query ** und der transponierten Matrix ** $ Key ^ T $ ** der fünf Wortvektoren genommen. Wenn Sie dann durch $ {\ sqrt {d_k}} $ dividieren und dann mit Softmax multiplizieren, können Sie das Gewicht (** Aufmerksamkeitsgewicht **) ermitteln, das angibt, welches Wort mit "I" in Beziehung steht und wie viel.
Der Grund für die Division durch $ {\ sqrt {d_k}} $ ist, dass bei einem zu großen Wert in der internen Produktberechnung, wenn Softmax multipliziert wird, die anderen Werte 0 werden können.
Als nächstes dominiert durch ** inneres Produkt ** von ** Aufmerksamkeitsgewicht ** und eine Matrix von fünf Wortvektoren ** Wert ** die Vektorkomponente von Wörtern, die eng mit "I" verwandt sind, dominant * * Kontextvektor kann berechnet werden. Dies ist die Berechnung der Aufmerksamkeit von "I".
Übrigens kann Trindformer eine parallele Berechnung durchführen und alle Abfragen gleichzeitig berechnen
Auf diese Weise wird die Aufmerksamkeitsberechnung aller Abfragen auf einmal abgeschlossen. Diese Berechnung wird durch die folgende Formel im Papier ausgedrückt.
Attention(Q, K, V)=softmax(\frac{QK^T}{\sqrt{d_k}})V
3) Multi-Head Attention
Eingabe in das skalierte Punktprodukt Achtung ** Abfrage, Schlüssel, Wert ** hat eine Struktur, in der die Ausgabe der vorherigen Stufe über jede vollständig verbundene Schicht eingeht. Mit anderen Worten wird die Ausgabe der vorherigen Stufe mit den Gewichten $ W_q, W_k bzw. W_v $ multipliziert. Zu diesem Zeitpunkt haben Sie nicht nur eine große Abfrage, einen Schlüssel, einen Wert (als Kopf bezeichnet), sondern mehrere kleine Abfrage-, Schlüssel- und Wertköpfe, und jeder Kopf hat einen latenten Ausdruck $ W_q, W_k, ** Multi-Head Attention ** zeigt, dass die Leistung verbessert wird, indem W_v $ berechnet und am Ende eins gemacht wird.
4) Musked Multi-Head Attention
Die Aufmerksamkeit auf der Decoderseite wird ebenfalls parallel berechnet, aber wenn bei der Berechnung der Aufmerksamkeit von "I", wenn "am", "a", "cat" im Berechnungsziel enthalten sind, wird das vorherzusagende Wort eingemacht. Maskieren Sie also das vorherige Wort im Schlüssel, um es unsichtbar zu machen. Multi-Head Attention mit dieser Funktion wird als ** Musked Multi-Head Attention ** bezeichnet.
5) Position-wise Feed-Forward Networks Dies ist eine Einheit, die die Merkmalsmenge der Ausgabe von der Aufmerksamkeitsebene mit zwei vollständig verbundenen Ebenen konvertiert. Die Eingabe ist (Anzahl der Wörter, Anzahl der eingebetteten Dimensionen von Wörtern), und das Produkt aus dieser und den Gewichten der beiden vollständig verbundenen Schichten ist die Ausgabe (Anzahl der Wörter, Anzahl der eingebetteten Dimensionen der Wörter). Wir haben es ** positionell ** genannt, weil es so aussieht, als gäbe es für jedes Wort ein unabhängiges neuronales Netzwerk.
Dieses Mal werden wir ein Modell implementieren, das die Klassifizierungsaufgabe lösen kann, indem wir die Aufmerksamkeit jedes Wortes des Satzes ** lernen, indem wir nur den Encoder auf der linken Seite des Transformer-Übersetzungsmodells verwenden. Bei der Priorisierung der Klarheit handelt es sich außerdem um Einzelkopfaufmerksamkeit, nicht um Mehrkopfaufmerksamkeit.
Der verwendete Datensatz ist ** IMDb ** (Internet Movie Dataset), der zusammenfasst, ob der Inhalt einer Filmkritik (auf Englisch) positiv oder negativ ist.
Indem Sie das Modell trainieren, bestimmen Sie bei der Eingabe einer Rezension eines Films **, ob die Rezension positiv oder negativ ist **, und geben Sie anhand der gegenseitigen Aufmerksamkeit der Rezensionswörter ** deutlich das Wort an, auf dem die Entscheidung basiert * * Lass mich das machen.
Dann möchte ich in der Reihenfolge von der Eingabe implementieren.
class Embedder(nn.Module):
'''Konvertiert das durch id angegebene Wort in einen Vektor'''
def __init__(self, text_embedding_vectors):
super(Embedder, self).__init__()
self.embeddings = nn.Embedding.from_pretrained(
embeddings=text_embedding_vectors, freeze=True)
# freeze=True wird nicht aktualisiert und ändert sich nicht in der Rückübertragung
def forward(self, x):
x_vec = self.embeddings(x)
return x_vec
Dies ist der Teil, der die Wort-ID mithilfe der nn.Embedding-Einheit von Pytorch in einen eingebetteten Vektor konvertiert.
class PositionalEncoder(nn.Module):
'''Fügen Sie Vektorinformationen hinzu, die die Position des eingegebenen Wortes angeben'''
def __init__(self, d_model=300, max_seq_len=256):
super().__init__()
self.d_model = d_model #Anzahl der Dimensionen des Wortvektors
#Erstellen Sie eine Wertetabelle als pe, die eindeutig durch die Reihenfolge der Wörter (pos) und die Position der Dimension des eingebetteten Vektors (i) bestimmt wird.
pe = torch.zeros(max_seq_len, d_model)
#An GPU senden, wenn GPU verfügbar ist
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
pe = pe.to(device)
for pos in range(max_seq_len):
for i in range(0, d_model, 2):
pe[pos, i] = math.sin(pos / (10000 ** ((2 * i)/d_model)))
pe[pos, i + 1] = math.cos(pos / (10000 ** ((2 * i)/d_model)))
#Fügen Sie die Mini-Batch-Dimension am Anfang der Tabelle pe hinzu
self.pe = pe.unsqueeze(0)
#Vermeiden Sie die Berechnung des Gradienten
self.pe.requires_grad = False
def forward(self, x):
#Fügen Sie Eingabe x und Positive Codierung hinzu
#x ist kleiner als pe, also mach es größer
ret = math.sqrt(self.d_model)*x + self.pe
return ret
Dies ist der Teil des Positionsgebers.
class Attention(nn.Module):
'''Transformator ist wirklich eine Mehrkopfaufmerksamkeit,
Klarheit wird Priorität eingeräumt und mit einer einzigen Aufmerksamkeit umgesetzt'''
def __init__(self, d_model=300):
super().__init__()
#SAGAN verwendete 1dConv, aber diesmal wird die Feature-Menge in die vollständig verbundene Ebene konvertiert.
self.q_linear = nn.Linear(d_model, d_model)
self.v_linear = nn.Linear(d_model, d_model)
self.k_linear = nn.Linear(d_model, d_model)
#Vollständig verbundene Schicht für die Ausgabe
self.out = nn.Linear(d_model, d_model)
#Variable zur Anpassung der Aufmerksamkeitsgröße
self.d_k = d_model
def forward(self, q, k, v, mask):
#Konvertieren Sie Features in vollständig verbundene Ebenen
k = self.k_linear(k)
q = self.q_linear(q)
v = self.v_linear(v)
#Berechnen Sie den Wert von Attention
#Wenn Sie jeden Wert hinzufügen, ist er zu groß, also root(d_k)Teilen und anpassen
weights = torch.matmul(q, k.transpose(1, 2)) / math.sqrt(self.d_k)
#Berechnen Sie hier die Maske
mask = mask.unsqueeze(1)
weights = weights.masked_fill(mask == 0, -1e9)
#Mit Softmax standardisieren
normlized_weights = F.softmax(weights, dim=-1)
#Aufmerksamkeit mit Wert multiplizieren
output = torch.matmul(normlized_weights, v)
#Konvertieren Sie Features in vollständig verbundene Ebenen
output = self.out(output)
return output, normlized_weights
Dies ist der Aufmerksamkeitsteil. Bei der Maskenberechnung sollte der Teil, in dem die Textdaten kurz sind und
class FeedForward(nn.Module):
def __init__(self, d_model, d_ff=1024, dropout=0.1):
'''Es ist eine Einheit, die einfach die Merkmalsmenge aus der Aufmerksamkeitsebene mit zwei vollständig verbundenen Ebenen konvertiert.'''
super().__init__()
self.linear_1 = nn.Linear(d_model, d_ff)
self.dropout = nn.Dropout(dropout)
self.linear_2 = nn.Linear(d_ff, d_model)
def forward(self, x):
x = self.linear_1(x)
x = self.dropout(F.relu(x))
x = self.linear_2(x)
return x
Dies ist der Feed Forward-Teil. Es ist eine einfache zweischichtige, vollständig verbundene Schicht.
class TransformerBlock(nn.Module):
def __init__(self, d_model, dropout=0.1):
super().__init__()
#Ebenennormalisierungsebene
# https://pytorch.org/docs/stable/nn.html?highlight=layernorm
self.norm_1 = nn.LayerNorm(d_model)
self.norm_2 = nn.LayerNorm(d_model)
#Aufmerksamkeitsschicht
self.attn = Attention(d_model)
#Zwei vollständig verbundene Schichten nach Aufmerksamkeit
self.ff = FeedForward(d_model)
# Dropout
self.dropout_1 = nn.Dropout(dropout)
self.dropout_2 = nn.Dropout(dropout)
def forward(self, x, mask):
#Normalisierung und Aufmerksamkeit
x_normlized = self.norm_1(x)
output, normlized_weights = self.attn(
x_normlized, x_normlized, x_normlized, mask)
x2 = x + self.dropout_1(output)
#Normalisierung und vollständig verbundene Schicht
x_normlized2 = self.norm_2(x2)
output = x2 + self.dropout_2(self.ff(x_normlized2))
return output, normlized_weights
Dies ist der Teil, der einen Transformatorblock durch Kombinieren von Attention und Feed Foward erstellt. Beide werden mit ** Layer Normalization ** und ** Dropout ** sowie ** Resid Binding ** ähnlich wie bei ResNet multipliziert.
class ClassificationHead(nn.Module):
'''Transformer_Verwenden Sie Blockausgabe und klassifizieren Sie schließlich'''
def __init__(self, d_model=300, output_dim=2):
super().__init__()
#Vollständig verbundene Schicht
self.linear = nn.Linear(d_model, output_dim) # output_dim ist zwei positive und negative
#Verarbeitung der Gewichtsinitialisierung
nn.init.normal_(self.linear.weight, std=0.02)
nn.init.normal_(self.linear.bias, 0)
def forward(self, x):
x0 = x[:, 0, :] #Extrahieren Sie die Merkmalsmenge (300 Dimensionen) des ersten Wortes jedes Satzes jeder Mini-Charge
out = self.linear(x0)
return out
Schließlich ist es der Teil, der ein negatives / positives Urteil abgibt. Durch Klassifizieren unter Verwendung der Merkmale des ersten Wortes jedes Satzes und Zurückpropagieren des Lernverlusts werden die Merkmale des ersten Wortes natürlich zu den Merkmalen, die das Negative / Positive des Satzes beurteilen.
class TransformerClassification(nn.Module):
'''Mit Transformator klassifizieren'''
def __init__(self, text_embedding_vectors, d_model=300, max_seq_len=256, output_dim=2):
super().__init__()
#Modellbau
self.net1 = Embedder(text_embedding_vectors)
self.net2 = PositionalEncoder(d_model=d_model, max_seq_len=max_seq_len)
self.net3_1 = TransformerBlock(d_model=d_model)
self.net3_2 = TransformerBlock(d_model=d_model)
self.net4 = ClassificationHead(output_dim=output_dim, d_model=d_model)
def forward(self, x, mask):
x1 = self.net1(x) #Wörter in Vektoren
x2 = self.net2(x1) #Positionsinformationen hinzufügen
x3_1, normlized_weights_1 = self.net3_1(
x2, mask) # Self-Konvertieren Sie Features mit Aufmerksamkeit
x3_2, normlized_weights_2 = self.net3_2(
x3_1, mask) # Self-Konvertieren Sie Features mit Aufmerksamkeit
x4 = self.net4(x3_2) #Klassifizierung 0 unter Verwendung des 0. Wortes der endgültigen Ausgabe-Ausgabe 1 Skalar
return x4, normlized_weights_1, normlized_weights_2
Dies ist der Teil, der schließlich das gesamte Modell unter Verwendung der bisher definierten Klassen erstellt.
Der gesamte Code wurde in Google Colab erstellt und auf Github veröffentlicht. Wenn Sie ihn also selbst ausprobieren möchten, klicken Sie auf diesen ** "Link" **. Sie können es verschieben, indem Sie auf transformer_en_run.ipynb) und oben auf dem angezeigten Blatt auf die Schaltfläche "Colab on Web" klicken.
Wenn du rennst
Auf diese Weise werden das Beurteilungsergebnis und seine Grundlage angezeigt.
Beim Surfen auf verschiedenen Websites gab es ein Beispiel für das Extrahieren von Sätzen aus dem Wertpapierbericht eines japanischen börsennotierten Unternehmens namens ** chABSA-Dataset **, das ein negatives / positives Urteil fällte und die Urteilsbasis anzeigte. Deshalb habe ich es auch in Google Colab zusammengefasst. Sah. Wenn Sie es selbst ausprobieren möchten, klicken Sie auf diesen ** "Link" ** und er befindet sich oben auf dem angezeigten Blatt. Sie können es verschieben, indem Sie auf die Schaltfläche "Colab on Web" klicken.
(Referenz) ・ Lernen während des Machens! Deep Learning von PyTorch ・ Ich habe eine Negativ / Positiv-Analyse-App mit Deep Learning (Python) erstellt [Teil 1] ・ Artikelkommentar Achtung ist alles, was Sie brauchen (Transformer)
Recommended Posts