Dank der Transformatoren in huggingface kann das japanische BERT-Modell jetzt sehr einfach mit PyTorch gehandhabt werden.
Viele Leute haben bereits Artikel über japanisches BERT mit umarmenden Gesichtern / Transformatoren veröffentlicht, aber ich habe beschlossen, nach dem Studium einen Artikel zu veröffentlichen.
[Lernen Sie beim Erstellen! Deep Learning von PyTorch](https://www.amazon.co.jp/%E3%81%A4%E3%81%8F%E3%82%8A%E3%81%AA%E3 % 81% 8C% E3% 82% 89% E5% AD% A6% E3% 81% B6-PyTorch% E3% 81% AB% E3% 82% 88% E3% 82% 8B% E7% 99% BA% E5 % B1% 95% E3% 83% 87% E3% 82% A3% E3% 83% BC% E3% 83% 97% E3% 83% A9% E3% 83% BC% E3% 83% 8B% E3% 83 Gepostet vom Autor von% B3% E3% 82% B0-% E5% B0% 8F% E5% B7% 9D% E9% 9B% 84% E5% A4% AA% E9% 83% 8E / dp / 4839970254) Die folgenden Artikel, die veröffentlicht wurden, sind überwiegend leicht zu verstehen. Er erklärt höflich die Orte, an denen BERT-Anfänger wie ich wahrscheinlich stecken bleiben.
In Bezug auf die obigen Bücher und Qiita-Artikel (oder fast das Kopieren) werde ich auch versuchen, die Satzklassifizierung durch BERT zu implementieren. Ich werde auch auf die Visualisierung durch Aufmerksamkeit eingehen. Für diejenigen, die Sätze vorerst mit BERT klassifizieren und die Visualisierung von Attention sehen möchten. Die Theorie von BERT berührt die Geschichte überhaupt nicht.
Behandeln Sie den Live-News-Korpus wie gewohnt als Validierungsdaten. Der Text der Livedoor-Nachrichten wird im Referenzartikel verwendet, aber es ist nicht interessant, ob er genau der gleiche ist. Daher entspricht der Titel des Livedoor-Nachrichtenkorpus dem von in der Vergangenheit geschriebener Artikel. Ich werde versuchen, Sätze nur mit zu klassifizieren.
Es ist in Google Colab sowie im Referenzartikel implementiert.
Mounten Sie zuerst Google Drive auf colab
from google.colab import drive
drive.mount('/content/drive')
Den Live-News-Korpus erhalten Sie unter hier. Speichern Sie den Datensatz mit dem Titel und der Kategorie des in Google Drive extrahierten Live-News-Korpus als DataFrame und speichern Sie ihn in Google Drive. Nach dem Speichern ist der Status der Überprüfung des Inhalts der Daten wie folgt.
import pickle
import pandas as pd
#Speicherort des Datensatzes
drive_dir = "drive/My Drive/Colab Notebooks/livedoor_data/"
with open(drive_dir + "livedoor_title_category.pickle", 'rb') as f:
livedoor_data = pickle.load(f)
livedoor_data.head()
#title category
#0 Bequemes Internet auch in Übersee! KDDI, "au Wi-Durch die Erweiterung von Fi SPOT wird es unterstützt-life-hack
#1 [Besonderheit/REISE] In ein aufregendes und sanftes arabisches Land (4)/8) livedoor-homme
#2 Twitter einer alleinstehenden Frau, überraschende Art, Dokujo zu genießen-tsushin
#3 Die Geschichte, dass die Pyramide in 20 Jahren gebaut wurde, ist ein Lügenfilm-enter
#4 Ayame Goriki präsentiert einen handgemachten Schokoladenkuchen mit dem Film "viel Liebe"-enter
Lassen Sie uns die Kategorie identifizieren.
#Rufen Sie eine Liste der Kategorien aus einem Datensatz ab
categories = list(set(livedoor_data['category']))
print(categories)
#['topic-news', 'movie-enter', 'livedoor-homme', 'it-life-hack', 'dokujo-tsushin', 'sports-watch', 'kaden-channel', 'peachy', 'smax']
#Erstellen Sie ein ID-Wörterbuch für eine Kategorie
id2cat = dict(zip(list(range(len(categories))), categories))
cat2id = dict(zip(categories, list(range(len(categories)))))
print(id2cat)
print(cat2id)
#{0: 'topic-news', 1: 'movie-enter', 2: 'livedoor-homme', 3: 'it-life-hack', 4: 'dokujo-tsushin', 5: 'sports-watch', 6: 'kaden-channel', 7: 'peachy', 8: 'smax'}
#{'topic-news': 0, 'movie-enter': 1, 'livedoor-homme': 2, 'it-life-hack': 3, 'dokujo-tsushin': 4, 'sports-watch': 5, 'kaden-channel': 6, 'peachy': 7, 'smax': 8}
#Kategorie-ID-Spalte zu DataFrame hinzugefügt
livedoor_data['category_id'] = livedoor_data['category'].map(cat2id)
#Mische nur für den Fall
livedoor_data = livedoor_data.sample(frac=1).reset_index(drop=True)
#Machen Sie das Dataset nur zu Titel- und Kategorie-ID-Spalten
livedoor_data = livedoor_data[['title', 'category_id']]
livedoor_data.head()
#title category_id
#0 Nainai Okamura weigert sich, das Erscheinen der AKB-Sondernummer "Wer erscheint an einem solchen Ort ..." zu beantragen. 0
#1 C-"Star Wars in Concert", in dem 3PO berühmte Szenen vorstellt, die in Japan gelandet sind 1
#2 Liefern Sie die Voyeur-Szene!?Es wurde ein schockierender Moment entdeckt, der eine kostenlose Veranstaltungssendung sein sollte [Thema] 6
#3 "An Mitsuhiro Oikawa in der letzten Folge meines Partners" Rücksichtslose Behandlung "" und der Frau selbst 0
#4 Über Hasebe und Kazu? Es gibt 5 überraschende Spieler in "Athleten, die Grundschüler mögen"
Da torchtext für die Datenvorverarbeitung verwendet wird, trennen Sie den Datensatz für Training und Test und speichern Sie ihn in einer tsv-Datei.
#Teilen Sie in Trainingsdaten und Testdaten
from sklearn.model_selection import train_test_split
train_df, test_df = train_test_split(livedoor_data, train_size=0.8)
print("Größe der Trainingsdaten", train_df.shape[0])
print("Testdatengröße", test_df.shape[0])
#Trainingsdatengröße 5900
#Testdatengröße 1476
#Als tsv-Datei speichern
train_df.to_csv(drive_dir + 'train.tsv', sep='\t', index=False, header=None)
test_df.to_csv(drive_dir + 'test.tsv', sep='\t', index=False, header=None)
Ich habe es in [hier] erwähnt (https://qiita.com/m__k/items/50b018c60f952c28869e), aber es scheint, dass bei der Installation von MeCab einige Vorsicht geboten ist. Wenn Sie pip wie unten gezeigt installieren, funktioniert es derzeit ohne Fehler.
#Bereiten Sie MeCab und Transformatoren vor
!apt install aptitude swig
!aptitude install mecab libmecab-dev mecab-ipadic-utf8 git make curl xz-utils file -y
#Mecab wie unten angegeben-Python3 Version 0.996.Wenn Sie es nicht auf 5 setzen, fällt es mit dem Token
# https://stackoverflow.com/questions/62860717/huggingface-for-japanese-tokenizer
!pip install mecab-python3==0.996.5
!pip install unidic-lite #Ohne dies schlägt es bei der Ausführung von MeCab mit einem Fehler fehl
!pip install transformers
Mit tokenizer.encode
können Sie das Teilungsschreiben ausführen, das mit dem japanischen BERT-Modell verwendet werden kann, und mit tokenizer.convert_ids_to_tokens
können Sie die in Morphologie und Unterwörter unterteilte ID-Zeichenfolge konvertieren. Sehr angenehm.
import torch
import torchtext
from transformers.modeling_bert import BertModel
from transformers.tokenization_bert_japanese import BertJapaneseTokenizer
#Deklariert einen Tokenizer für die Freigabe von japanischem BERT
tokenizer = BertJapaneseTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
#Ich werde versuchen, es zu teilen.
text = list(train_df['title'])[0]
wakati_ids = tokenizer.encode(text, return_tensors='pt')
print(tokenizer.convert_ids_to_tokens(wakati_ids[0].tolist()))
print(wakati_ids)
print(wakati_ids.size())
#['[CLS]', 'Höhe', 'Aber', 'Niedrig', 'Weiblich', 'Ist', 'Ehe', 'Zu', 'Ungünstig', '?', '[SEP]']
#tensor([[ 2, 7236, 14, 3458, 969, 9, 1519, 7, 9839, 2935, 3]])
#torch.Size([1, 11])
Das japanische Vorlernmodell der Tohoku-Universität, das vom umarmenden Gesicht aus gehandhabt werden kann, hat bis zu 512 morphologische Primzahlen (Anzahl der Unterwörter) von Sätzen. Wenn also das Formularelement der zu verarbeitenden Daten und die Anzahl der Unterwörter 512 überschreiten, geben Sie "max_length" bis 512 an. Für die Titel dieses Live-News-Korpus beträgt die maximale Anzahl jedoch 76 (siehe unten), sodass "max_length" diesmal nicht angegeben wird.
#Die Länge der Sätze, die vom japanischen BERT verarbeitet werden können, beträgt 512, aber die maximale Länge der Titel von Livedoor-Nachrichten beträgt CLS.,76 auch mit SEP-Token
import seaborn as sns
title_length = livedoor_data['title'].map(tokenizer.encode).map(len)
print(max(title_length))
# 76
sns.distplot(title_length)
Erstellen Sie einen Iterator wie folgt. Da die Größe von "tokenizer.encode" "(1 x Satzlänge)" ist, muss "[0]" angegeben werden.
#Erstellen Sie mit torchtext einen Iterator für Trainingsdaten und Testdaten
def bert_tokenizer(text):
return tokenizer.encode(text, return_tensors='pt')[0]
TEXT = torchtext.data.Field(sequential=True, tokenize=bert_tokenizer, use_vocab=False, lower=False,
include_lengths=True, batch_first=True, pad_token=0)
LABEL = torchtext.data.Field(sequential=False, use_vocab=False)
train_data, test_data = torchtext.data.TabularDataset.splits(
path=drive_dir, train='train.tsv', test='test.tsv', format='tsv', fields=[('Text', TEXT), ('Label', LABEL)])
#BERT scheint eine Mini-Batch-Größe von 16 oder 32 zu verwenden, aber der Livedoor-Titel hat eine kurze Satzlänge, sodass sogar 32 auf Colab funktionieren.
BATCH_SIZE = 32
train_iter, test_iter = torchtext.data.Iterator.splits((train_data, test_data), batch_sizes=(BATCH_SIZE, BATCH_SIZE), repeat=False, sort=False)
Lassen Sie uns vorher die Eingabe- und Ausgabeformate des erlernten japanischen BERT überprüfen. Das BERT-Modell kann einfach wie folgt in einer Zeile deklariert werden. Zu bequem
from transformers.modeling_bert import BertModel
model = BertModel.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
Sie können die Struktur von BERT sehen, indem Sie das Modell selbst "drucken". Die Ausgabe ist lang, also halten Sie sie geschlossen.
BertModel(
(embeddings): BertEmbeddings(
(word_embeddings): Embedding(32000, 768, padding_idx=0)
(position_embeddings): Embedding(512, 768)
(token_type_embeddings): Embedding(2, 768)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(encoder): BertEncoder(
(layer): ModuleList(
(0): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(1): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(2): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(3): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(4): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(5): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(6): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(7): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(8): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(9): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(10): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(11): BertLayer(
(attention): BertAttention(
(self): BertSelfAttention(
(query): Linear(in_features=768, out_features=768, bias=True)
(key): Linear(in_features=768, out_features=768, bias=True)
(value): Linear(in_features=768, out_features=768, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
)
(output): BertSelfOutput(
(dense): Linear(in_features=768, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
(intermediate): BertIntermediate(
(dense): Linear(in_features=768, out_features=3072, bias=True)
)
(output): BertOutput(
(dense): Linear(in_features=3072, out_features=768, bias=True)
(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
)
(pooler): BertPooler(
(dense): Linear(in_features=768, out_features=768, bias=True)
(activation): Tanh()
)
)
Wie Sie diesem Ergebnis entnehmen können, gibt es eine Einbettungsebene, die zuerst Wörter in Vektoren umwandelt, und dann gibt es 12 Bert-Ebenen. Sie können auch bestätigen, dass die Anzahl der Vektordimensionen des Wortes und die Anzahl der Dimensionen der darin verborgenen Ebene 768 Dimensionen beträgt.
Lassen Sie uns die Eingabe- und Ausgabeformate von BertModel anhand der Referenz überprüfen.
Das Eingabeformat des BERT-Modells wird als "(batch_size, sequence_length)" geschrieben. Die Ausgabe scheint standardmäßig last_hidden_state und pooler_output zurückzugeben, aber das Aufmerksamkeitsgewicht scheint durch Angabe von "output_attentions = True" erhalten zu werden. Attention gibt alle Ergebnisse jeder der 12 Multi-Head-Aufmerksamkeiten im 12-Layer-BertLayer zurück.
#Aus dem oben erstellten Testdateniterator
batch = next(iter(test_iter))
print(batch.Text[0].size())
# torch.Size([32, 48]) ←(batch_size, sequence_length)
#Ausgabe während der BERT-Vorwärtsausbreitung_attentions=Sie können Aufmerksamkeitsgewicht mit True erhalten
last_hidden_state, pooler_output, attentions = model(batch.Text[0], output_attentions=True)
print(last_hidden_state.size())
print(pooler_output.size())
print(len(attentions), attentions[-1].size())
#torch.Size([32, 48, 768]) ← (batch_size, sequence_length×hidden_size)
#torch.Size([32, 768])
#12 torch.Size([32, 12, 48, 48]) ← (batch_size, num_heads, sequence_length, sequence_length)
Beim Erfassen eines Satzvektors mit BERT wird der Vektor des cls-Tokens am Anfang jedes Wortvektors von last_hidden_state als Satzvektor betrachtet und verwendet.
Nachdem wir die Eingabe- und Ausgabeformate des BERT-Modells irgendwie verstanden haben, werden wir ein Modell erstellen, das Sätze mithilfe von BERT klassifiziert. Wie im Referenzartikel ist es meiner Meinung nach einfacher, die Struktur zu verstehen und zu studieren, indem Sie sie selbst implementieren, anstatt die Bibliothek für die Klassifizierung zu verwenden, die von huggingface bereitgestellt wird, also Klassifizierung Implementieren ohne die Bibliothek zu benutzen.
from torch import nn
import torch.nn.functional as F
from transformers.modeling_bert import BertModel
class BertClassifier(nn.Module):
def __init__(self):
super(BertClassifier, self).__init__()
self.bert = BertModel.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
#Die versteckte Ebene von BERT hat 768 Dimensionen,9 Livedoor News Kategorien
self.linear = nn.Linear(768, 9)
#Verarbeitung der Gewichtsinitialisierung
nn.init.normal_(self.linear.weight, std=0.02)
nn.init.normal_(self.linear.bias, 0)
def forward(self, input_ids):
# last_hidden_Erhalten Sie Zustand und Aufmerksamkeit
vec, _, attentions = self.bert(input_ids, output_attentions=True)
#Holen Sie sich nur den Vektor der ersten Token-Cls
vec = vec[:,0,:]
vec = vec.view(-1, 768)
#Konvertieren Sie die Abmessungen für die Klassifizierung in vollständig verbundene Ebenen
out = self.linear(vec)
return F.log_softmax(out), attentions
classifier = BertClassifier()
Ich habe bis jetzt noch keine Feinabstimmung durchgeführt, aber wie im Referenzartikel schalte ich alle Parameter einmal aus und aktualisiere dann nur die Teile, in denen ich die Parameter aktualisieren möchte. Ich habe viel gelernt. Was die Lernrate betrifft, so wurde die letzte Schicht von BERT bereits vorab gelernt, sodass sie nur geringfügig aktualisiert wird und die letzte vollständig verbundene Schicht, die zur Klassifizierung eingefügt wird, eine höhere Lernrate aufweist. Ich sehe ich sehe.
#Feinabstimmung der Einstellungen
#Führen Sie die Gradientenberechnung nur für das letzte BertLayer-Modul und den hinzugefügten Klassifizierungsadapter durch
#Zunächst einmal AUS
for param in classifier.parameters():
param.requires_grad = False
#Aktualisieren Sie nur die letzte Ebene von BERT ON
for param in classifier.bert.encoder.layer[-1].parameters():
param.requires_grad = True
#Die Klassenklassifizierung ist ebenfalls aktiviert
for param in classifier.linear.parameters():
param.requires_grad = True
import torch.optim as optim
#Die Lernrate ist für den vorgelernten Teil klein und für die letzte vollständig verbundene Schicht groß.
optimizer = optim.Adam([
{'params': classifier.bert.encoder.layer[-1].parameters(), 'lr': 5e-5},
{'params': classifier.linear.parameters(), 'lr': 1e-4}
])
#Einstellungen der Verlustfunktion
loss_function = nn.NLLLoss()
Wie im Referenzartikel ist es eigentlich besser, im Trainingsmodus und im Überprüfungsmodus getrennt zu schreiben, aber vorerst möchte ich es verschieben, sodass ich nur den minimalen Code durchlaufe, um Folgendes zu lernen. .. Die endgültige Genauigkeit änderte sich nicht wesentlich, ob die Anzahl der Epochen 5 oder 10 betrug, daher habe ich die Anzahl der Epochen auf 5 gesetzt. Der Verlust nimmt stetig ab, daher ist es vorerst in Ordnung.
#GPU-Einstellungen
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#Netzwerk an GPU senden
classifier.to(device)
losses = []
#Die Anzahl der Epochen beträgt 5
for epoch in range(5):
all_loss = 0
for idx, batch in enumerate(train_iter):
batch_loss = 0
classifier.zero_grad()
input_ids = batch.Text[0].to(device)
label_ids = batch.Label.to(device)
out, _ = classifier(input_ids)
batch_loss = loss_function(out, label_ids)
batch_loss.backward()
optimizer.step()
all_loss += batch_loss.item()
print("epoch", epoch, "\t" , "loss", all_loss)
#epoch 0 loss 246.03703904151917
#epoch 1 loss 108.01931090652943
#epoch 2 loss 80.69403756409883
#epoch 3 loss 62.87365382164717
#epoch 4 loss 50.78619819134474
Schauen wir uns die F-Punktzahl an. Der Text des Artikels scheint 90% zu überschreiten, aber die Klassifizierung nur des Titels ergab 85%. Obwohl der Titel eine zusammenfassende Bedeutung für den Artikel hat, war ich oft zu 85% an diesem kurzen Satz interessiert.
from sklearn.metrics import classification_report
answer = []
prediction = []
with torch.no_grad():
for batch in test_iter:
text_tensor = batch.Text[0].to(device)
label_tensor = batch.Label.to(device)
score, _ = classifier(text_tensor)
_, pred = torch.max(score, 1)
prediction += list(pred.cpu().numpy())
answer += list(label_tensor.cpu().numpy())
print(classification_report(prediction, answer, target_names=categories))
# precision recall f1-score support
#
# topic-news 0.80 0.82 0.81 158
# movie-enter 0.85 0.82 0.83 178
#livedoor-homme 0.68 0.73 0.70 108
# it-life-hack 0.88 0.82 0.85 179
#dokujo-tsushin 0.82 0.85 0.84 144
# sports-watch 0.89 0.87 0.88 180
# kaden-channel 0.91 0.97 0.94 180
# peachy 0.78 0.77 0.78 172
# smax 0.94 0.91 0.92 177
#
# accuracy 0.85 1476
# macro avg 0.84 0.84 0.84 1476
# weighted avg 0.85 0.85 0.85 1476
Lassen Sie uns abschließend die Grundlage für die Beurteilung der Satzklassifizierung überprüfen, indem wir die Aufmerksamkeit visualisieren. Das zu visualisierende Aufmerksamkeitsgewicht aktualisierte die Parameter der letzten Ebene von BertLayer beim Einstellen der Feinabstimmung, dh das Aufmerksamkeitsgewicht der letzten Ebene wurde für diese Titelklassifizierung gelernt, sodass das Aufmerksamkeitsgewicht der letzten Ebene ist Es scheint, dass es als Grundlage für die Beurteilung dieser Aufgabe verwendet werden kann.
Das diesmal deklarierte BertClassifer-Modell gibt alle Aufmerksamkeitsgewichte zurück. Holen Sie sich also nur die letzte Ebene wie folgt und überprüfen Sie die Größe erneut.
batch = next(iter(test_iter))
score, attentions = classifier(batch.Text[0].to(device))
#Holen Sie sich nur das Aufmerksamkeitsgewicht der letzten Ebene und überprüfen Sie die Größe
print(attentions[-1].size())
# torch.Size([32, 12, 48, 48])
Als ich die Referenz erneut überprüfte, war die Bedeutung dieser Größe "(batch_size, num_heads, sequence_length, sequence_length)". Da die Aufmerksamkeit von BertEncoder die Selbstaufmerksamkeit ist, wie viel Aufmerksamkeit wird jedem Wort der zweiten Sequenzlänge für jedes Wort der ersten Sequenzlänge geschenkt. Dieses Mal wurden die Sätze unter Verwendung des ersten Tokens cls klassifiziert. Wenn man sich also vorstellt, auf welches Wort der Vektor des ersten Tokens achtet, kann dies als Grundlage für die Beurteilung dieser Aufgabe angesehen werden. Darüber hinaus beträgt die Selbstaufmerksamkeit von BERT 12 Aufmerksamkeiten mit mehreren Köpfen. Wenn ich sie visualisiere, füge ich alle 12 Aufmerksamkeitsgewichte hinzu und verwende sie.
Ich habe versucht, den Visualisierungsteil wie folgt unter Bezugnahme auf das Nachschlagewerk zu implementieren.
def highlight(word, attn):
html_color = '#%02X%02X%02X' % (255, int(255*(1 - attn)), int(255*(1 - attn)))
return '<span style="background-color: {}">{}</span>'.format(html_color, word)
def mk_html(index, batch, preds, attention_weight):
sentence = batch.Text[0][index]
label =batch.Label[index].item()
pred = preds[index].item()
label_str = id2cat[label]
pred_str = id2cat[pred]
html = "Richtige Antwortkategorie: {}<br>Vorhersagekategorie: {}<br>".format(label_str, pred_str)
#Deklarieren Sie den Tensor Null für die Länge des Satzes
seq_len = attention_weight.size()[2]
all_attens = torch.zeros(seq_len).to(device)
for i in range(12):
all_attens += attention_weight[index, i, 0, :]
for word, attn in zip(sentence, all_attens):
if tokenizer.convert_ids_to_tokens([word.tolist()])[0] == "[SEP]":
break
html += highlight(tokenizer.convert_ids_to_tokens([word.numpy().tolist()])[0], attn)
html += "<br><br>"
return html
batch = next(iter(test_iter))
score, attentions = classifier(batch.Text[0].to(device))
_, pred = torch.max(score, 1)
from IPython.display import display, HTML
for i in range(BATCH_SIZE):
html_output = mk_html(i, batch, pred, attentions[-1])
display(HTML(html_output))
Hier sind einige Visualisierungsergebnisse.
――Yodobashi Camera Umeda Store ist durch Unterwörter unterteilt, aber es bezieht sich auf Haushaltsgeräte, so dass es eine teilweise, aber Aufmerksamkeit ist.
--peachy (Artikel über die Liebe zu Frauen). Das ist auch schön.
»Wurden Sie im richtigen Gespräch von Peachy mitgerissen?
Ich habe hauptsächlich die Guten vorgestellt, aber ehrlich gesagt dachte ich, dass es insgesamt eine heikle Aufmerksamkeit war. (Ich mache mir Sorgen, ob die Implementierung wirklich korrekt ist ...) Ich war jedoch daran interessiert, dass es erstaunlich ist, auf die Teile zu achten, die nicht so gut sind, selbst wenn sie in Unterwörter unterteilt sind.
Dank Huggingface / Transformers und Referenzartikeln kann ich BERT bewegen, wenn auch irgendwie. Ich möchte BERT für verschiedene Aufgaben verwenden
Ende