[PYTHON] Ich habe versucht, Satzklassifizierung und Aufmerksamkeitsvisualisierung durch das japanische BERT mit PyTorch zu implementieren

Einführung

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.

Referenz

[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.

Problemstellung

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.

Implementierung

Es ist in Google Colab sowie im Referenzartikel implementiert.

Datenaufbereitung

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)

Installieren Sie MeCab und Huggingface / Transformatoren

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

Erstellen Sie einen Iterator mit torchtext

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)

Deklaration des Klassifizierungsmodells

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.

BERT-Modellstruktur
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()

Feinabstimmung der Einstellungen

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()

Lernen

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

Genauigkeitsprüfung

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

Aufmerksamkeitsvisualisierung

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. image.png

--peachy (Artikel über die Liebe zu Frauen). Das ist auch schön. image.png

»Wurden Sie im richtigen Gespräch von Peachy mitgerissen? image.png

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.

abschließend

Dank Huggingface / Transformers und Referenzartikeln kann ich BERT bewegen, wenn auch irgendwie. Ich möchte BERT für verschiedene Aufgaben verwenden

Ende

Recommended Posts

Ich habe versucht, Satzklassifizierung und Aufmerksamkeitsvisualisierung durch das japanische BERT mit PyTorch zu implementieren
Ich habe versucht, die Satzklassifizierung durch Self Attention mit PyTorch zu implementieren
Ich habe versucht, die Genauigkeit der japanischen BERT- und der japanischen Distil-BERT-Satzklassifizierung mit PyTorch & Einführung der BERT-Technik zur Verbesserung der Genauigkeit zu vergleichen
Ich habe versucht, PLSA in Python zu implementieren
Ich habe versucht, Permutation in Python zu implementieren
Ich habe versucht, PLSA in Python 2 zu implementieren
Ich habe versucht, ADALINE in Python zu implementieren
Ich habe versucht, PPO in Python zu implementieren
Ich habe versucht, CVAE mit PyTorch zu implementieren
Ich habe versucht, die Bayes'sche lineare Regression durch Gibbs-Sampling in Python zu implementieren
Ich habe versucht, das Lesen von Dataset mit PyTorch zu implementieren
[PyTorch] Einführung in die Klassifizierung japanischer Dokumente mit BERT
Ich habe versucht, eine selektive Sortierung in Python zu implementieren
Ich habe versucht, einen Pseudo-Pachislot in Python zu implementieren
Ich habe versucht, Drakues Poker in Python zu implementieren
Ich habe versucht, GA (genetischer Algorithmus) in Python zu implementieren
Ich habe versucht, SSD jetzt mit PyTorch zu implementieren (Dataset)
Ich habe versucht, PCANet zu implementieren
Ich habe versucht, StarGAN (1) zu implementieren.
[Django] Ich habe versucht, Zugriffsbeschränkungen durch Klassenvererbung zu implementieren.
Ich habe versucht, MNIST nach GNN zu klassifizieren (mit PyTorch-Geometrie).
Ich habe versucht, die Mail-Sendefunktion in Python zu implementieren
Ich habe versucht, das Blackjack of Trump-Spiel mit Python zu implementieren
Ich habe versucht, SSD jetzt mit PyTorch zu implementieren (Modellversion)
Ich habe versucht, Deep VQE zu implementieren
Ich habe versucht, eine kontroverse Validierung zu implementieren
Ich habe versucht, Pytorchs Datensatz zu erklären
Ich habe versucht, Realness GAN zu implementieren
Ich habe versucht, ein missverstandenes Gefangenendilemma in Python zu implementieren
Ich habe versucht, Autoencoder mit TensorFlow zu implementieren
[PyTorch] Einführung in die Dokumentklassifizierung mit BERT
Ich habe versucht, Trumps Kartenspiel in Python zu implementieren
Ich habe versucht, alle Python-Visualisierungstools zusammenzufassen, die von aktiven Doktoranden in der Forschung verwendet wurden [Anwendung]
Ich habe versucht, die Zusammenführungssortierung in Python mit möglichst wenigen Zeilen zu implementieren
[PyTorch] Verwendung von BERT - Feinabstimmung japanischer vorab trainierter Modelle zur Lösung von Klassifizierungsproblemen
Ich habe versucht, die Veränderung der Schneemenge für 2 Jahre durch maschinelles Lernen vorherzusagen
Ich habe versucht, ein scheinbar Windows-Snipper-Tool mit Python zu implementieren
Ich habe versucht, die Blasensortierung nach Sprache zu programmieren
Ich habe versucht, durch Schaben ein Bild zu bekommen
Ich habe versucht, Keras in TFv1.1 zu integrieren
Ich habe versucht, Drachenkugeln nach Adalin zu klassifizieren
Ich habe versucht, das Problem des Handlungsreisenden umzusetzen
[Keras] Ich habe versucht, das Problem der Klassifizierung des Donut-Typ-Bereichs durch maschinelles Lernen zu lösen. [Studie]
[Einführung] Ich habe versucht, es selbst zu implementieren, während ich den Dichotomiebaum erklärte
[Serie für vielbeschäftigte Personen] Ich habe versucht, mit einer Syntaxanalyse zusammenzufassen, um Nachrichten in 30 Sekunden aufzurufen
[Einführung] Ich habe versucht, es selbst zu implementieren, während ich erklärte, um die Dichotomie zu verstehen
[Erklärung zur Implementierung] Verwendung der japanischen Version von BERT in Google Colaboratory (PyTorch)
Ich habe versucht, die in Python installierten Pakete grafisch darzustellen
Ich habe versucht, ein multivariates statistisches Prozessmanagement (MSPC) zu implementieren.
Ich habe versucht, Mine Sweeper auf dem Terminal mit Python zu implementieren
Ich habe versucht, künstliches Perzeptron mit Python zu implementieren
[Einführung in Pytorch] Ich habe versucht, Cifar10 mit VGG16 ♬ zu kategorisieren
Ich habe versucht zusammenzufassen, wie man Pandas von Python benutzt
Ich habe versucht, Grad-CAM mit Keras und Tensorflow zu implementieren
Ich habe versucht, einen automatischen Nachweis der Sequenzberechnung zu implementieren
Django super Einführung von Python-Anfängern! Teil 6 Ich habe versucht, die Login-Funktion zu implementieren