[PYTHON] Klassifizieren Sie Qiita-Posts ohne morphologische Analyse mit Tweet2Vec

Inhaltsverzeichnis

»Was ich tun wollte

Was ich machen wollte

――Ich möchte kurze japanische Dokumente (Tweets usw.) klassifizieren.)

Bei der Verarbeitung von SNS-Posts usw. treten viele Tippfehler, Auslassungen, Slang, neue Wörter, Piktogramme, Gesichtszeichen, Fremdsprachen, Fachbegriffe, Notationsschwankungen usw. auf. Daher scheint der Ansatz mit einem morphologischen Analysegerät nachteilig zu sein. Beim Lesen der jüngsten NLP-Artikel scheint es, dass sie auf Zeichenebene statt auf Wortebene lernen. Folgen wir also diesem Trend. Ich denke, Japanisch ist vorteilhafter als Englisch, weil es eine große Menge an Informationen pro Zeichen enthält.

Da das Dokument nicht zu lang ist und es schwierig erscheint, die Morphologie zu analysieren, habe ich beschlossen, Qiitas Beiträge nur nach Titel als Thema mit einem gewissen Grad an Kohäsivität im Thema zu klassifizieren. Der Text von Qiita ist Markdown oder HTML, was schwierig zu handhaben scheint, daher werde ich ihn dieses Mal nicht verwenden.

Holen Sie sich Qiita Beiträge

Verwenden Sie Qiita API.

Da der Text und andere Metainformationen möglicherweise in Zukunft verwendet werden, habe ich den gesamten von der API in PostgreSQL zurückgegebenen JSON mithilfe von Peewee in Python gespeichert.


from peewee import *
from playhouse.postgres_ext import PostgresqlExtDatabase, BinaryJSONField

db = PostgresqlExtDatabase(
    'mytable',
    user='myuser',
    password='mypassword',
    register_hstore=False,
    autocommit=True,
    autorollback=True
)
db.connect()

#Modelldefinition
class BaseModel(Model):
    """A base model that will use our Postgresql database"""
    class Meta:
        database = db

class QiitaItem(BaseModel):
    item_id = CharField(unique=True)
    created_at = DateTimeField(index=True)
    raw = BinaryJSONField()
    
    class Meta:
        db_table = 'qiita_items'

#Tabelle erstellen
db.create_tables([QiitaItem])

Aufruf der Qiita-API

Beachten Sie, dass die Anzahl der Anrufe gering ist, wenn Sie keine Token verwenden. Beachten Sie, dass selbst wenn Sie Token verwenden, das Limit 1.000 Anfragen pro Stunde beträgt.

import json, requests

url = 'http://qiita.com/api/v2/items'
headers = {'Authorization': 'Bearer myauthorizationtoken1234567890'}

#Vorerst die letzten 10,Beispiel für das Abrufen von 000 Posts
#Für weitere Beiträge kann für Parameter das gleiche Abfragefeld wie für die Websuche verwendet werden. Ich werde mein Bestes geben, indem ich es vollständig nutze.
for i in range(100):
    resp = requests.get(url, params={'per_page': 100, 'page': 1+i}, headers=headers)
    for item in resp.json():
        #Wenn ein Nullzeichen vorhanden ist, schlägt das Speichern in JSONB fehl. Entfernen Sie es daher.
        item['body'] = item['body'].strip('\x00')
        item['rendered_body'] = item['rendered_body'].strip('\x00')
        try:
            QiitaItem.create_or_get(item_id=item['id'], created_at=item['created_at'], raw=item)
        except DataError:
            print(False)

Verwenden Sie Tweet2Vec

Zunächst möchte ich den Titel von Qiita, der verschiedene Längen hat, in eine Vektordarstellung mit einer bestimmten Anzahl von Dimensionen umwandeln. Sobald ein Vektorraummodell verwendet wird, können verschiedene Methoden des maschinellen Lernens angewendet werden.

Anfangs habe ich gensims Doc2Vec ausprobiert, aber die Genauigkeit war nicht gut.

Als Ergebnis der Suche in verschiedenen Artikeln fand ich eine Methode mit RNN (GRU) namens Tweet2Vec. Darüber hinaus ist der Code auf GitHub verfügbar. In der Originalarbeit wurden 2 Millionen Tweets in 2000 Arten von Hash-Tags eingeteilt, und die Genauigkeit war höher als bei der wortbasierten Methode.

Die meisten Beiträge von Qiita haben mehrere Tags, und die Länge des Textes entspricht in etwa der des Tweets. Ich dachte, er könnte verwendet werden.

Tweet2Vec ist in Theano + Lasagne (Python2.7) implementiert.

Erstellen einer Datendatei

Verwendet 393 verschiedene Tags mit über 100 Posts. Von den ungefähr 63.000 Posts mit diesen Tags waren 5% zum Testen, 5% für den Lebenslauf und die restlichen 90% zum Lernen bestimmt.

Beiträge in Englisch und Chinesisch sind ebenfalls gemischt, aber ich schließe sie nicht besonders aus. Das Originalpapier verwendete nur englische Tweets und enthielt eine Vorverarbeitung zum Konvertieren von Groß- und Kleinbuchstaben. Dies wird jedoch auch nicht durchgeführt.

Die Trainingsdaten enthalten 2281 Zeichentypen, und es scheint, dass die Anzahl der Eingabedimensionen erheblich geringer ist als bei Verwendung von BOW.


from collections import Counter
from random import shuffle
import io

cnt = Counter()
for item in QiitaItem.select():
    for tag in item.raw['tags']:
        cnt[tag['name']] += 1

common_tags = [name for name, c in cnt.most_common(393)]

samples_all = []

for item in QiitaItem.select():
    tags = [tag['name'] for tag in item.raw['tags']]
    intersection = list(set(common_tags) & set(tags))
    if len(intersection) > 1:
        samples_all.append((item, intersection))
        
shuffle(samples_all)
n_all = len(samples_all)
n_test = n_cv = int(n_all / 20)
n_train = n_all - n_test - n_cv
samples_train = samples_all[:n_train]
samples_test = samples_all[n_train:(n_train+n_test)]
samples_cv = samples_all[(n_train+n_test):]

with io.open('misc/qiita_train.txt', 'w') as f:
    for item, tags in samples_train:
        for tag in tags:
            f.write(tag + '\t' + item.raw['title'] + '\n')

with io.open('misc/qiita_test.txt', 'w') as f:
    for item, tags in samples_test:
        f.write(','.join(tags) + '\t' + item.raw['title'] + '\n')

with io.open('misc/qiita_cv.txt', 'w') as f:
    for item, tags in samples_cv:
        f.write(','.join(tags) + '\t' + item.raw['title'] + '\n')

Lauf

Es stehen drei Arten von Shell-Skripten zur Verfügung. Schreiben Sie den darin enthaltenen Dateipfad neu und führen Sie ihn aus.

Beim Lernen

./tweet2vec_trainer.sh

Wenn Sie die Genauigkeit sehen möchten

./tweet2vec_tester.sh

Wenn Sie ein Tag hinzufügen möchten (für ein unbekanntes korrektes Tag)

./tweet2vec_encoder.sh

Als Einschränkung

Verwenden Sie eine GPU-Instanz

Als ich als Test mit 8.000 Zeilen Trainingsdatendatei trainierte, dauerte es auf meinem MacBook ungefähr 2,7 Stunden. Die endgültige Trainingsdatendatei umfasst ungefähr 140.000 Zeilen. Wenn Sie sie auf einem lokalen Computer trainieren, dauert die Fertigstellung 48 Stunden. Daher habe ich beschlossen, eine GPU-Instanz in AWS einzurichten.

Installieren Sie Theano auf einer GPU-Instanz von AWS unter Bezugnahme auf diesen Artikel. Ich war ungeduldig, weil matplotlib nicht mit pip eingegeben wurde, sondern mit sudo apt-get install python-matplotlib.

Wenn die Verbindung in der Mitte unterbrochen wird, ist dies ein Problem. Überprüfen Sie sie und führen Sie sie aus. Danach stürzte das Terminal ab, als wollte ich es anstreben, aber dank dessen war es sicher.

screen -S "qiita"
./tweet2vec_trainer.sh

Fertig in 12 Epochen für ca. 3,5 Stunden. Die Gebühr hat sich gelohnt, da sie schätzungsweise zehnmal schneller war.

Ergebnis der Tag-Vorhersage

Genauigkeit der Tag-Vorhersage

Testdaten mit 72,10% Genauigkeit, Eine Genauigkeit von 70,49% wurde auch mit CV-Daten erzielt, auf die während des Trainings überhaupt nicht Bezug genommen wurde.

./tweet2vec_tester.sh (qiita_test.txt)

Precision @ 1 = 0.721040939384
Recall @ 10 = 0.777895906062
Mean rank = 4.29197080292

./tweet2vec_tester.sh (qiita_cv.txt)

Precision @ 1 = 0.704855601396
Recall @ 10 = 0.779064847138
Mean rank = 4.05744208188

Die Genauigkeit der Hashtag-Vorhersage von Tweet im Originalpapier betrug etwa 24%, daher scheint sie ziemlich genau zu sein, obwohl das Thema sehr unterschiedlich ist.

Das Folgende ist ein Beispiel für die Vorhersage von Tags für CV-Daten.

Ein Beispiel, das gut vorhergesagt werden kann

# Post-Titel Tatsächliches Tag Voraussichtliches Tag(TOP 10)
1 So führen Sie ein Java-Programm in Maven aus Java,Maven Maven,Java,java8,Eclipse,gradle,Android,Tomcat,Groovy,JavaEE,JUnit
2 Asynchrone Endlosschleife in JavaScript JavaScript,Node.js JavaScript,HTML,jQuery,Node.js,CSS,HTML5,CoffeeScript,js,Ajax,es6
3 Was tun, vom Kauf eines Mac bis zur Entwicklung von Rails? Mac,Rails Ruby,Rails,Mac,Rails4,rbenv,RubyOnRails,MacOSX,Zsh,homebrew,Gem
4 [Unity] Wissens-UI, die durch die Entwicklung von Spielen für Smartphones bei einer Spielefirma gewonnen wurde .NET,Unity3D,C#,Unity Unity,C#,Unity3D,.NET,Android,iOS,LINQ,VisualStudio,Android-Entwicklung,Java
5 R Sprache-High Performance Computing R,statistics R,statistics,Mathematik,Numerische Berechnung,Datenanalyse,Ruby,Statistiken,Verarbeitung natürlicher Sprache,Python,NLP R Sprache
6 Ersteinstellung des Sakura VPS-Servers und Aufbau der LAMP-Umgebung PHP,MacOSX,Sakura VPS Sakura VPS,vps,Apache,CentOS,PHP,MySQL,Linux,CentOS6.x,WordPress,postfix
7 Rahmen mit Animation(StoryBoard) Xcode,Objective-C iOS,Swift,Storyboard,Xcode,Objective-C,Xcode6,Android,UI,iOS8,iPhone
8 Willkommen bei Apples Reject Hell, das ich durch die Erstellung einer Dating-App gefunden habe Xcode,iOS iOS,Swift,Xcode,Objective-C,Android,iPhone,CocoaPods,JavaScript,Mac,AdventCalendar
9 Zeigen Sie nicht transparente Elemente in durchscheinenden Elementen an HTML,CSS CSS,HTML,HTML5,CSS3,JavaScript,jQuery,bootstrap,Android,js,Java
10 Für Docker japanischer Golang 做 1 einzelner chinesischer Satz golang,docker Go,golang,docker,Ruby,Slack,vagrant,Rails,GitHub,OSX,Erlang

―― Wenn der Tag-Name im Titel so wie er ist angezeigt wird, kann er grundsätzlich korrekt als Top-Kandidat aufgeführt werden.

Beispiele, die nicht gut vorhergesagt werden können

# Post-Titel Tatsächliches Tag Voraussichtliches Tag(TOP 10)
1 Ein kompletter Amateur nahm am ISUCON 5-Hauptrennen teil golang,MySQL,Go,nginx iOS,Unity,C#,Objective-C,JavaScript,Swift,Ruby,.NET,IoT,JSON
2 Speisepilze identifizieren MachineLearning,Python,matplotlib Linux,iOS,ShellScript,Ruby,Python,Objective-C,CentOS,PHP,Bash,Swift
3 Schalten Sie den japanischen Eingang in gnome3 auf Alt Alt(AX-Tastaturstil) CentOS,Linux JavaScript,Java,OSX,homebrew,Mac,api,HTML,Node.js,MacOSX,Künstliche Intelligenz
4 Statische Dateien werden zwischengespeichert (wenn nicht Browser-Cache) VirtualBox,Apache JavaScript,IoT,CSS,Chrome,firefox,MacOSX,Windows,HTML5,jQuery,Arduino
5 Benutzername der Person, die mit dem gewünschten Hashtag getwittert hat(@Hinter dem Teil)Bekommen Twitter,TwitterAPI,Gem,Ruby Ruby,Rails,AWS,JavaScript,Python,Go,Java,jq,golang,PHP
6 Formular im Blick: could not find implicit value for parameter messages: play.api.i18n.Was tun, wenn Nachrichten angezeigt werden? Scala,PlayFramework Mac,MacOSX,OSX,Xcode,Android,Linux,Ruby,Ubuntu,Java,Windows

――Natürlich ist die Vorhersage nicht gut, wenn der Titel zu wenig oder zu mehrdeutig ist.

Versuchen Sie, einen ähnlichen Beitrag zu veröffentlichen

Tweet2Vec gibt eine Vektordarstellung jedes Beitragstitels an. Lassen Sie uns also einen ähnlichen Beitrag basierend auf dem Kosinusabstand ausgeben.

Ändern Sie encode_char.py ein wenig, damit die erforderlichen Dateien ausgegeben werden.

encode_char.py



  print("Encoding...")
  out_data = []
  out_pred = []
  out_emb = []
  numbatches = len(Xt)/N_BATCH + 1
  for i in range(numbatches):
      xr = Xt[N_BATCH*i:N_BATCH*(i+1)]
      x, x_m = batch.prepare_data(xr, chardict, n_chars=n_char)
      p = predict(x,x_m)
      e = encode(x,x_m)
      ranks = np.argsort(p)[:,::-1]

      for idx, item in enumerate(xr):
          out_data.append(item)
          out_pred.append(' '.join([inverse_labeldict[r] for r in ranks[idx,:5]]))
          out_emb.append(e[idx,:])

  # Save
  print("Saving...")
  with open('%s/data.pkl'%save_path,'w') as f:
      pkl.dump(out_data,f)
  with io.open('%s/predicted_tags.txt'%save_path,'w') as f:
      for item in out_pred:
          f.write(item + '\n')
  with open('%s/embeddings.npy'%save_path,'w') as f:
      np.save(f,np.asarray(out_emb))

Es werden ungefähr 130.000 Qiita-Posts verwendet, einschließlich Posts, die nicht zum Lernen oder Testen verwendet wurden.

with io.open('../misc/qiita_all_titles.txt', 'w') as f:
    for item in QiitaItem.select():
        f.write(item.raw['title'] + '\n')

Schreiben Sie neu und führen Sie es aus, damit das Ergebnis in ein Verzeichnis namens qiita_result_all ausgegeben wird.

./tweet2vec_encoder.sh

Es dauerte ungefähr 3 Stunden, als ich es auf meinem lokalen Computer ausführte. Ich hätte auch dafür eine GPU-Instanz verwenden sollen.

Der Python-Code, der ähnliche Beiträge anzeigt, sieht folgendermaßen aus.

import cPickle as pkl
import numpy as np
from scipy import spatial
import random

with io.open('qiita_result_all/data.pkl', 'rb') as f:
    titles = pkl.load(f)

with io.open('qiita_result_all/embeddings.npy','r') as f:
    embeddings = np.load(f)

n = len(titles)
def most_similar(idx):
    sims = [(i, spatial.distance.cosine(embeddings[idx],embeddings[i])) for i in range(n) if i != idx]
    sorted_sims = sorted(sims, key=lambda sim: sim[1])
    print titles[idx]
    for sim in sorted_sims[:5]:
        print "%.3f" % (1 - sim[1]), titles[sim[0]]

Ausführungsergebnis

Die Zahlen in der linken Spalte sind die Ähnlichkeit (1 ist das Maximum, -1 ist das Minimum) und die rechte Spalte ist der Titel.

>>> most_similar(random.randint(0,n-1))
Mehrere Google Maps und JQuery Wrap()Geheimnisvolles Phänomen bei der Verwendung
0.678 Kombinieren Sie mehrere Google-Kalender
0.619 [GM] Ich möchte den Breiten- und Längengrad der auf Google Map gesuchten Adressen kennen.
0.601 Google Maps schnell anzeigen (API v3-Version)
0.596 Geschwindigkeitsmessung bei gleichzeitiger Verwendung von jQuery Sizzle und Filter-API in einem Smartphone-Browser
0.593 Anzeigen von Google Map auf der Website von einer Adresse aus

>>> most_similar(random.randint(0,n-1))
Ich möchte nur scipy hinzufügen, aber es ist ein lustiges Memo. Ubuntu,Python3.
0.718 Installation von SciPy und matplotlib(Python)
0.666 SciPy+Erstellen Sie mit matplotlib ein 3D-Streudiagramm(Python)
0.631 scipy ist Python 2.7.Wenn es 8 ist, stolpert die Pip-Installation
0.622 [Anmerkung] zukünftige Aussage ~ Python ~
0.Versuchen Sie es mit 610 scipy

>>> most_similar(random.randint(0,n-1))
Verschieben Sie den mehrzeiligen UI-Beschriftungstext nach links oben
0.782 IB richtet den UI-Beschriftungstext aus
0.Exportieren Sie 699 NGUI UI Label Zeichen für Zeichen
0.624 UILabel-Text in Swift zentrieren
0.624 Fügen Sie einen Zeilenumbruch von IB in UILabel-Text ein
0.624 Synchronisieren Sie das Scrollen von zwei UITableViews

>>> most_similar(random.randint(0,n-1))
[Unity]Was tun, bevor Sie Ihr Projekt mit git oder svn verwalten?
0.810 Festlegen von Elementen beim Verwalten von Git mit Unity
0.800 [Unity]Ignorieren Sie Dateien bei der Verwaltung mit git(.gitignore)Aufbau
0.758 [Unity]Ein einfaches Beispiel für den Austausch von Nachrichten über ein Netzwerk
0.751 [Grundfunktion von Unity] Praktische V-Taste und Schnappverschluss beim Erstellen eines Karten-Dungeons
0.746 [Unity]So laden Sie eine Textdatei mit einem Skript und zeigen deren Inhalt an

>>> most_similar(random.randint(0,n-1))
Zeichnen eines Kreises mit einer Rotationsmatrix in Java
0.911 Java Strichzeichnung mit Rotationsmatrix
0.898 Gegenseitige Konvertierung von Ganzzahlen und Bytearrays in Java
0.897 Fibonacci-Sequenz in Java anzeigen
0.896 Zeichnen eines Kreises mit einer Java 2D-Rotationsmatrix
0.873 Bestätigte Effizienz der Java-Zeichenfolgenverknüpfung

>>> most_similar(random.randint(0,n-1))
Liste der Songs, die auf WWDC14 abgespielt werden
0.830 Teilnahme an WWDC15
0.809 WWDC 2014 Hinweis
0.Ich habe versucht, 797 WWDC 2015 zusammenzufassen
0.772 Zusammenfassung der Fragen, die ich auf der WWDC 2015 gestellt habe
0.744 WWDC 2016 abgelehnte E-Mail war eine erfolgreiche E-Mail

Rücksichtnahme und Herausforderungen

――Ich bin sehr dankbar, dass dieses Maß an Genauigkeit einfach durch Training ohne morphologische Analyse, ohne Erstellung eines Benutzerwörterbuchs und fast ohne Vorverarbeitung erreicht werden kann. ――Kann es auf Dokumente angewendet werden, die in natürlicher Sprache schwieriger zu verarbeiten sind, wie z. B. den Qiita-Text? ――Dieses Mal habe ich Tweet2Vec so wie es ist verwendet, also möchte ich den Inhalt genau verstehen und ein besseres Modell erstellen. ――Wenn Sie es als tatsächliches Produkt verwenden, möchten Sie auch die Hyperparameter optimieren. Ich möchte verschiedene Metainformationen kombinieren, die ich dieses Mal ignoriert habe.

Recommended Posts

Klassifizieren Sie Qiita-Posts ohne morphologische Analyse mit Tweet2Vec
[Python] Morphologische Analyse mit MeCab
Japanische morphologische Analyse mit Python
[PowerShell] Morphologische Analyse mit SudachiPy
Text Mining mit Python ① Morphologische Analyse
Ich habe mit Mecab gespielt (morphologische Analyse)!
Python: Vereinfachte morphologische Analyse mit regulären Ausdrücken
Text Mining mit Python ① Morphologische Analyse (re: Linux-Version)
Machen Sie mit LINE + Flask einen morphologischen Analyse-Bot
Sammeln von Informationen von Twitter mit Python (morphologische Analyse mit MeCab)
Klassifizieren Sie Artikel mit Tags, die von Qiita durch unbeaufsichtigtes Lernen angegeben wurden