Ich habe die Aufgabe, den Autor anhand der Arbeit aus dem Aozora Bunko zu schätzen, und habe sie als Artikel geschrieben. Der Code ist hier verfügbar [https://github.com/minnsou/aozora_pred].
Der Fluss, den ich diesmal gemacht habe, ist wie folgt.
wget
herunterDie Bibliothek verwendet hauptsächlich BeautifulSoup, Keras, Mecab, Gensim. Ich werde diese Installationsmethoden weglassen, da sie vom Hauptpunkt abweichen. Grundsätzlich war pip
ziemlich gut.
Entscheiden Sie zunächst, wer die Arbeit herunterladen soll. Dieses Mal haben wir uns vorerst an Personen gewandt, die "__ den Autor der Zeile " und " die Anzahl der veröffentlichten Werke 20 oder mehr __" erfüllen.
Holen Sie sich die author ID, die zum Herunterladen der Arbeit von Aozora Bunko Writer List erforderlich ist. Zum Beispiel ist Ryunosuke Akutagawa 879.
Erstellen Sie "autors.txt", die diese zusammenfasst. Es wäre schön gewesen, dies automatisch zu erstellen, aber da die Anzahl der Personen gering war, habe ich es manuell erstellt.
authors.txt
Ryunosuke Akutagawa 879
Takeo Arishima 25
Andersenhans Christian 19
Ishikawa Tatsuki 153
Jun Ishihara 1429
Izumi Kyoka 50
Mansaku Itami 231
Sachio Ito 58
Noeda Ito 416
Satoshi Ueda 235
Uemura Matsuzono 355
Uchida Rouan 165
Juzo Unno 160
Ranpo Edogawa 1779
Yu Okubo 10
Shigenobu Okuma 1879
Katsura Omachi 237
Asajiro Oka 1474
Kanoko Okamoto 76
Okamoto Kido 82
Miaki Ogawa 1475
Hideo Oguma 124
Oguri Mushitaro 125
Sakunosuke Oda 40
Nobuo Origuchi 933
Insgesamt 25 Personen. Zwischen dem Namen und der Autoren-ID befindet sich ein halbes Feld. Darüber hinaus gibt es eine Person (Yoko Okura), die aufgrund des später beschriebenen Problems aus dieser Liste gestrichen wurde.
Der Rest importiert die erforderlichen Bibliotheken. Nachfolgende Python-Skripte sind dieselben wie die unter author_prediction.ipynb veröffentlichten.
from bs4 import BeautifulSoup
import re
import MeCab
from gensim.models.doc2vec import Doc2Vec
from gensim.models.doc2vec import TaggedDocument
import numpy as np
import matplotlib.pyplot as plt
from keras import layers
from keras import models
from keras import optimizers
from keras.utils import np_utils
Damit ist die Vorbereitung abgeschlossen.
wget
herunterVerwenden Sie zunächst author.txt, um die Arbeits-ID für jeden Autor abzurufen. Speichern Sie dies als personID ??. Txt
(??
ist die Autoren-ID).
# authors.Basierend auf txt, wget und personID mit Arbeits-ID??.Generiere txt(??Ist die Personen-ID)
# personID_Setzen Sie personID in die Liste ein
personID_list = []
memo = open('./authors.txt')
for line in memo:
line = line.rstrip()
line = line.split( )
#print(line)
author = line[0]
personID = line[1]
personID_list.append(personID)
# authors.Wget den Index basierend auf der personID von txt (es ist nicht notwendig, dies zu tun, da es bereits erstellt wurde)
#!wget https://www.aozora.gr.jp/index_pages/person{personID}.html -O ./data/index{personID}.html
#!sleep 1
#Gespeicherter Index??.Öffnen Sie HTML
with open("./data/index{}.html".format(personID), encoding="utf-8") as f:
soup = BeautifulSoup(f)
ol = soup.find("ol").text
bookID = re.findall('ID:[0-9]*', ol) # index??.Holen Sie sich den Teil, in dem die Arbeits-ID geschrieben ist, aus dem HTML-Code
#print(bookID)
bookID_list = []
for b in bookID:
b = b[3:] # 'ID:'Löschen
bookID_list.append(b) #Arbeits-ID hinzufügen
#print(bookID_list)
print('author {}\tpersonID {}\tnumber of cards {}'.format(author, personID, len(bookID_list)))
# bookID_Erstellen Sie eine Textdatei, die die Arbeits-ID eines bestimmten Autors basierend auf der Liste beschreibt (dies ist nicht erforderlich, da sie bereits erstellt wurde).
#with open('./data/personID{}.txt'.format(personID), mode='w') as f:
# for b in bookID_list:
# f.write(b + ' ')
Wenn Sie es ausführen, erhalten Sie eine Ausgabe wie diese. Dadurch wird eine Datei mit dem Namen personID ??. Txt
für 25 Personen erstellt (??
ist die Autoren-ID).
Autor Ryunosuke Akutagawa personID 879 Anzahl der Karten 376
Autor Takeo Arishima personID 25 Kartenanzahl 44
Autor Andersenhans Christian personID 19 Anzahl Karten 23
Autor Ishikawa Tatsuki personID 153 Anzahl der Karten 78
Autor Jun Ishihara personID 1429 Anzahl der Karten 24
Autor Izumi Kyoka personID 50 Kartenanzahl 208
Autor Mansaku Itami personID 231 Anzahl der Karten 23
Autor Sachio Ito personID 58 Anzahl der Karten 39
Autor Ito Noeda personID 416 Anzahl der Karten 80
Autor Satoshi Ueda personID 235 Anzahl der Karten 53
Autor Uemura Matsuzono personID 355 Anzahl der Karten 83
Autor Uchida Roan personID 165 Anzahl der Karten 26
Autor Juzo Unno personID 160 Kartenanzahl 177
Autor Ranpo Edogawa personID 1779 Anzahl der Karten 91
Autor Yu Okubo personID 10 Kartenanzahl 68
Autor Shigenobu Okuma personID 1879 Anzahl der Karten 31
Autor Katsura Omachi personID 237 Anzahl der Karten 60
Autor Asajiro Oka personID 1474 Anzahl der Karten 25
Autor Kanoko Okamoto personID 76 Anzahl der Karten 119
Autor Okamoto Kido personID 82 Anzahl der Karten 247
Autor Miaki Ogawa personID 1475 Anzahl der Karten 521
Autor Hideo Oguma personID 124 Anzahl Karten 33
Autor Oguri Mushitaro personID 125 Kartenanzahl 22
Autor Sakunosuke Oda personID 40 Kartenanzahl 70
Autor Nobuo Origuchi personID 933 Anzahl der Karten 197
Wenn Sie im obigen Skript "#" entfernen und "wget" selbst erstellen, um "personID ??. Txt" zu erstellen, erhalten Sie eine mit mehr Arbeits-IDs als die hochgeladene "personID ??. Txt`. Ich werde. Dies liegt daran, dass die Arbeits-ID, die beim Extrahieren des Texts mit dem folgenden Skript einen __ Fehler verursacht, manuell gelöscht wird __.
Zum Beispiel gibt es in der Arbeit "Apple Pie" von Yu Okubo zusätzlich zu der üblichen Aozora Bunko-Site einen externen Link. Es ist eingefügt und ich werde es bekommen. Ich wünschte, ich könnte diese Blue Sky Library Site bekommen, aber [externe Site](http: //p.booklog. jp / book / 35337) wird genommen.
Ebenso wie Hideo Ogumas Short Songbook existiert der Text nicht (<div class =" main_text ">
Einige haben keine Tags), was ebenfalls einen Fehler verursacht.
Ich habe die gelöschte personID ??. Txt
für eine so außergewöhnliche Arbeits-ID hochgeladen. Wenn Sie sie also vorerst verschieben möchten, können Sie das Kommentieren von __ vermeiden. Nur diejenigen, die den Vorgang überprüfen möchten, sollten das Speicherverzeichnis __ auskommentieren und ändern.
Laden Sie als Nächstes die Arbeit mit der in personID ??. Txt
geschriebenen Arbeits-ID herunter. Ich verwende pubserver2, um die Arbeit herunterzuladen, aber jetzt, wo ich darüber nachdenke, könnte ich einfach "wget" machen, ohne pubserver2 zu durchlaufen?
# personID??.Holen Sie sich die Arbeits-ID von txt und bringen Sie die Arbeit mit wget (bis zu 50 Werke pro Autor)
#Der Name des HTML-Codes, in dem die Arbeit geschrieben ist, ist Text_x_y.html(x ist personID, y ist bookID)
for personID in personID_list:
print('personID', personID)
with open("./data/personID{}.txt".format(personID), encoding="utf-8") as f:
for bookID_str in f:
bookID_list = bookID_str.split( )
print('number of cards', len(bookID_list))
#Wenn es zu viele Werke gibt, wird es einige Zeit dauern, also beschränken Sie sich auf 50 Werke
if len(bookID_list) >= 50:
bookID_list = bookID_list[:50]
for bookID in bookID_list:
print('ID', bookID)
#Erstellen Sie eine HTML-Datei, die den Text beschreibt, indem Sie anhand der bookID aufrufen (dies ist nicht erforderlich, da er bereits erstellt wurde).
#!wget http://pubserver2.herokuapp.com/api/v0.1/books/{bookID}/content?format=html -O ./data/text{personID}_{bookID}.html
#!sleep 1
Erstellen Sie eine Funktion zum Formatieren und Kennzeichnen des Texts. Dies basiert auf hier. Den Tags werden Nummern von 0 bis 24 in der Reihenfolge "autoren.txt" zugewiesen (Ryunosuke Akutagawa ist 0, Takeo Arishima ist 1, ..., Nobuo Origuchi ist 24). Wenn Sie die Autoren-ID wie für die Tag-Nummer verwenden, ist dies beim Generieren von Daten problematisch. Daher werden wir sie hier neu nummerieren.
# doc(Der Text der Arbeit)Wörter nur mit Verben, Adjektiven und Nomenklatur auflisten
#Generieren Sie ein mit Tags versehenes Dokument, das aus Wörtern und Tags besteht
def split_into_words(doc, name=''):
mecab = MeCab.Tagger("-Ochasen")
lines = mecab.parse(doc).splitlines() #Morphologische Analyse
words = []
for line in lines:
chunks = line.split('\t')
#Fügen Sie nur Nomenklaturen (ohne Zahlen), Verben und Adjektive hinzu
if len(chunks) > 3 and (chunks[3].startswith('Verb') or chunks[3].startswith('Adjektiv') or (chunks[3].startswith('Substantiv') and not chunks[3].startswith('Substantiv-Nummer'))):
words.append(chunks[0])
#print(words)
return TaggedDocument(words=words, tags=[name])
Generieren Sie train_text mit Trainingsdaten und test_text mit Auswertungsdaten.
#Trainiere um zu lernen_Text generieren (fest auf 20 Werke pro Autor)
#Test zum Testen_Erstellen Sie auch Text (verwenden Sie alle anderen Daten, die nicht zum Lernen verwendet wurden).
#Da die Anzahl der Arbeiten von Person zu Person unterschiedlich ist, testen Sie_Die Anzahl der im Text enthaltenen Werke variiert ebenfalls von Person zu Person
train_text = []
test_text = []
for i, personID in enumerate(personID_list):
print('personID', personID)
with open("./data/personID{}.txt".format(personID), encoding="utf-8") as f:
for bookID_str in f:
#print(bookID)
bookID_list = bookID_str.split( )
#Ich habe nicht mehr als 50 Werke heruntergeladen, also habe ich es geschnitten
if len(bookID_list) >= 50:
bookID_list = bookID_list[:50]
print('number of cards', len(bookID_list))
for j, bookID in enumerate(bookID_list):
#Öffnen Sie das HTML, das den zuvor gespeicherten Text enthält
soup = BeautifulSoup(open("./data/text{}_{}.html".format(personID, bookID), encoding="shift_jis"))
#Der Text ist geschrieben<div>Mitnahme
main_text = soup.find("div", "main_text").text
#print(main_text)
#Die ersten 20 Werke sind Zug_Setzen Sie es in Text, der Rest ist Test_Text einfügen
if j < 20:
train_text.append(split_into_words(main_text, str(i)))
print('bookID\t{}\ttrain'.format(bookID))
else:
test_text.append(split_into_words(main_text, str(i)))
print('bookID\t{}\ttest'.format(bookID))
Erstellen Sie ein Modell für Doc2Vec. Hyperparameter wie Alpha und Epochen sind ziemlich gut definiert.
#Erstellen und trainieren Sie ein Modell von Doc2Vec
model = Doc2Vec(vector_size=len(train_text), dm=0, alpha=0.05, min_count=5)
model.build_vocab(train_text)
model.train(train_text, total_examples=len(train_text), epochs=5)
#Lernergebnisse speichern
#model.save('./data/doc2vec.model')
Erstellen Sie Daten, indem Sie den Text in einen numerischen Vektor für das Training mit einem neuronalen Netz konvertieren.
#Erstellen Sie Daten für das neuronale Netz aus dem erstellten Modell und Text, einer Liste mit markierten Dokumenten.
def text2xy(model, text):
x = []
y = []
for i in range(len(text)):
#print(i)
vec = model.infer_vector(text[i].words) #In einen Zahlenvektor konvertieren
x.append(vec.tolist())
y.append(int(text[i].tags[0]))
x = np.array(x)
y = np_utils.to_categorical(y) #Konvertieren Sie Tag-Nummern in onehot
return x, y
#Erstellung von Trainingsdaten und Bewertungsdaten
x_train, y_train = text2xy(model, train_text)
x_test, y_test = text2xy(model, test_text)
Bereiten Sie eine Funktion (dens_train) vor, die von der Modellerstellung bis zum Training ausgeführt wird, und eine Funktion (draw_acc und draw_loss) zum Zeichnen.
Das Modell des von mir erstellten neuronalen Netzes ist ein ziemlich einfaches Modell, das aus drei vollständig verbundenen Schichten besteht.
Für die Verlustfunktion verwendeten wir eine kategoriale Kreuzentropie, die häufig bei Klassifizierungsproblemen verwendet wird. Die Anzahl der Einheiten und die Lernrate sind angemessen.
def dense_train(epochs):
#Modelldefinition
kmodel = models.Sequential()
kmodel.add(layers.Dense(512, activation='relu', input_shape=(500,)))
kmodel.add(layers.Dense(256, activation='relu'))
kmodel.add(layers.Dense(25, activation='softmax'))
kmodel.summary()
#Modell kompilieren
kmodel.compile(loss='categorical_crossentropy', optimizer=optimizers.RMSprop(lr=1e-4), metrics=['acc'])
#Modelllernen
history = kmodel.fit(x=x_train, y=y_train, epochs=epochs, validation_data=(x_test, y_test))
#Modell speichern
#model.save('./data/dense.h5')
return history, kmodel
#Korrektes Diagramm der Antwortrate
def draw_acc(history):
acc = history.history['acc']
val_acc = history.history['val_acc']
epochs = range(1, len(acc) + 1)
fig = plt.figure()
fig1 = fig.add_subplot(111)
fig1.plot(epochs, acc, 'bo', label='Training acc')
fig1.plot(epochs, val_acc, 'b', label='Validation acc')
fig1.set_xlabel('epochs')
fig1.set_ylabel('accuracy')
fig.legend(bbox_to_anchor=(0., 0.19, 0.86, 0.102), loc=5) #Das zweite Argument des Ankers (Legende) ist y, das dritte Argument ist x
#Bild speichern
fig.savefig('./acc.pdf')
plt.show()
#Verlustplot
def draw_loss(history):
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(loss) + 1)
fig = plt.figure()
fig1 = fig.add_subplot(111)
fig1.plot(epochs, loss, 'bo', label='Training loss')
fig1.plot(epochs, val_loss, 'b', label='Validation loss')
fig1.set_xlabel('epochs')
fig1.set_ylabel('loss')
fig.legend(bbox_to_anchor=(0., 0.73, 0.86, 0.102), loc=5) #Das zweite Argument des Ankers (Legende) ist y, das dritte Argument ist x
#Bild speichern
#fig.savefig('./loss.pdf')
plt.show()
Trainiere und zeichne ein Diagramm der richtigen Antwortrate. Diesmal betrug die Anzahl der Epochen 10.
history, kmodel = dense_train(10)
draw_acc(history)
Die erhaltene Ausgabe ist wie folgt.
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 512) 256512
_________________________________________________________________
dense_2 (Dense) (None, 256) 131328
_________________________________________________________________
dense_3 (Dense) (None, 25) 6425
=================================================================
Total params: 394,265
Trainable params: 394,265
Non-trainable params: 0
_________________________________________________________________
Train on 500 samples, validate on 523 samples
Epoch 1/10
500/500 [==============================] - 0s 454us/step - loss: 3.0687 - acc: 0.1340 - val_loss: 2.9984 - val_acc: 0.3308
Epoch 2/10
500/500 [==============================] - 0s 318us/step - loss: 2.6924 - acc: 0.6300 - val_loss: 2.8255 - val_acc: 0.5698
Epoch 3/10
500/500 [==============================] - 0s 315us/step - loss: 2.3527 - acc: 0.8400 - val_loss: 2.6230 - val_acc: 0.6864
Epoch 4/10
500/500 [==============================] - 0s 283us/step - loss: 1.9961 - acc: 0.9320 - val_loss: 2.4101 - val_acc: 0.7610
Epoch 5/10
500/500 [==============================] - 0s 403us/step - loss: 1.6352 - acc: 0.9640 - val_loss: 2.1824 - val_acc: 0.8088
Epoch 6/10
500/500 [==============================] - 0s 237us/step - loss: 1.2921 - acc: 0.9780 - val_loss: 1.9504 - val_acc: 0.8337
Epoch 7/10
500/500 [==============================] - 0s 227us/step - loss: 0.9903 - acc: 0.9820 - val_loss: 1.7273 - val_acc: 0.8432
Epoch 8/10
500/500 [==============================] - 0s 220us/step - loss: 0.7424 - acc: 0.9840 - val_loss: 1.5105 - val_acc: 0.8642
Epoch 9/10
500/500 [==============================] - 0s 225us/step - loss: 0.5504 - acc: 0.9840 - val_loss: 1.3299 - val_acc: 0.8623
Epoch 10/10
500/500 [==============================] - 0s 217us/step - loss: 0.4104 - acc: 0.9840 - val_loss: 1.1754 - val_acc: 0.8719
Klicken Sie hier für ein Diagramm der Lernergebnisse.
Nach 6 Epochen fühle ich mich krank, aber am Ende habe ich eine korrekte Antwortrate von __87,16% __. Obwohl ich nicht viel Hyperparameter-Tuning durchgeführt habe, habe ich das Gefühl, einen anständigen Prozentsatz an richtigen Antworten zu erhalten.
Hyperparameter-Tuning und Modellauswahl (LSTM verwenden oder so?) Kann mehr getan werden. Bitte kommentieren Sie, wenn Sie Vorschläge haben. Danke für Ihren Besuch.
Recommended Posts