4 minutes
今さら言語処理100本ノック #6 前半
50. データの入手・整形
News Aggregator Data Setをダウンロードし、以下の要領で学習データ(train.txt),検証データ(valid.txt),評価データ(test.txt)を作成せよ.
- ダウンロードした zip ファイルを解凍し,readme.txt の説明を読む.
- 情報源(publisher)が"Reuters”, “Huffington Post”, “Businessweek”, “Contactmusic.com”, “Daily Mail"の事例(記事)のみを抽出する.
- 抽出された事例をランダムに並び替える.
- 抽出された事例の 80%を学習データ,残りの 10%ずつを検証データと評価データに分割し,それぞれ train.txt,valid.txt,test.txt というファイル名で保存する.ファイルには,1行に1事例を書き出すこととし,カテゴリ名と記事見出しのタブ区切り形式とせよ(このファイルは後に問題 70 で再利用する).
学習データと評価データを作成したら,各カテゴリの事例数を確認せよ.
ダウンロードと解凍
$ curl -O https://archive.ics.uci.edu/ml/machine-learning-databases/00359/NewsAggregatorDataset.zip
$ unzip NewsAggregatorDataset.zip
$ cat readme.txt
メインコード
with open("newsCorpora.csv") as f:
lines = f.readlines()
# 2. 記事の抽出
def filter_articles(lines):
allow_publishers = ["Reuters", "Huffington Post", "Businessweek", "Contactmusic.com", "Daily Mail"]
for line in lines:
_, title, _, publisher, category, *_ = line.split("\t")
if publisher in allow_publishers:
yield "{}\t{}\n".format(category, title)
allow_articles = list(filter_articles(lines))
print(len(allow_articles)) #> 13356
# 3. 並び替え
import random
random.seed(0)
random.shuffle(allow_articles)
# 4. データの分割と書き出し
def write_articles(fname, lines):
with open(fname, mode='x') as f:
f.write("".join(lines))
len_allow_articles = len(allow_articles)
s1, s2 = int(len_allow_articles*0.8), int(len_allow_articles*0.9)
train, valid, test = allow_articles[:s1], allow_articles[s1:s2], allow_articles[s2:]
write_articles("train.txt", train)
write_articles("valid.txt", valid)
write_articles("test.txt", test)
! cat train.txt | wc -l # 10684
! cat valid.txt | wc -l # 1336
! cat test.txt | wc -l # 1336
memo
おそらく csv 形式の問題でpandas
を使って読み込むと読み込み後の行数が変わる。
$ cat newsCorpora.csv | wc -l # 422937
import pandas as pd
df = pd.read_csv("newsCorpora.csv",sep="\t", header=None)
print(len(df.index)) #> 422419
検証
df = pd.read_csv("newsCorpora.csv",sep="\t", header=None, engine="python", error_bad_lines=False)
# エラー一部抽出
Skipping line 9180: ' ' expected after '"'
Skipping line 10581: ' ' expected after '"'
他の方法
cat newsCorpora.csv | awk -F '\t' '{print $4}'
とかでうまく分けられると思う。
各カテゴリ事例数
echo "train.txt"; cut -f 1 train.txt | sort | uniq -c | sort -r; echo;
echo "valid.txt"; cut -f 1 valid.txt | sort | uniq -c | sort -r; echo;
echo "test.txt"; cut -f 1 test.txt | sort | uniq -c | sort -r; echo;
train.txt
4557 b
4180 e
1203 t
744 m
valid.txt
543 b
542 e
159 t
92 m
test.txt
572 e
527 b
163 t
74 m
51. 特徴量抽出
学習データ,検証データ,評価データから特徴量を抽出し,それぞれ train.feature.txt,valid.feature.txt,test.feature.txt というファイル名で保存せよ. なお,カテゴリ分類に有用そうな特徴量は各自で自由に設計せよ.記事の見出しを単語列に変換したものが最低限のベースラインとなるであろう.
いまいち何をすれば良いのかわからないので以下の処理をしようと思う
- 記事見出しのトークン化
- クリーニングとフィルタリング
- レンマ化
中間ファイルの作成
import spacy
def generate_middle(fname1, fname2):
with open (fname1) as f:
lines = f.readlines()
nlp = spacy.load("en_core_web_sm")
text = ""
for line in lines:
category, topic = line.rstrip("\n").split("\t")
doc = nlp(topic)
tokens = [e.lemma_ for e in doc if e.pos_ not in ["PUNCT", "SYM", "NUM", "SPACE"] and not e.is_stop]
text += "{}\t{}\n".format(category, " ".join(tokens))
with open (fname2, mode='x') as f:
f.write(text)
generate_middle("train.txt", "train.middle.txt")
$ diff <(head -n 5 train.txt) <(head -n 5 train.middle.txt)
1,5c1,5
< t White House Science Fair Will Focus On Girls In STEM
< b US STOCKS-Wall St dips after Fed's Bullard talks about rates
< m UPDATE 1-Some on downed Malaysian plane were heading to AIDS conference
< b FOREX-Euro struggles after German data, nears 2-year low vs sterling
< b Fitch Affirms Tula Region at 'BB'; Outlook Stable
---
> t White House Science Fair focus girl STEM
> b stock Wall St dip Fed Bullard talk rate
> m UPDATE down malaysian plane head AIDS conference
> b FOREX Euro struggle german datum near low vs sterling
> b Fitch Affirms Tula Region BB Outlook Stable
train データを元に特徴量作成
from gensim import corpora
with open("train.middle.txt") as f:
lines = f.readlines()
docs = [line.rstrip("\n").split("\t")[1] for line in lines]
tokens = [doc.split(" ") for doc in docs]
dct = corpora.Dictionary(tokens)
print(len(dct)) #> 13945
dct.filter_extremes(no_below=2)
print(len(dct)) #> 7469
dct.save_as_text('train.feature.dict.txt')
参考: https://tedboy.github.io/nlps/generated/generated/gensim.corpora.Dictionary.filter_extremes.html
参考: https://qiita.com/tatsuya-miyamoto/items/f505dfa8d5307f8c6e98
52. 学習
51 で構築した学習データを用いて,ロジスティック回帰モデルを学習せよ.
トレーニングデータ、検証データの読み込み
from itertools import chain
def read_f(path):
with open(path) as f:
lines = f.readlines()
docs = [line.rstrip("\n").split("\t") for line in lines]
flatten_docs = list(chain.from_iterable(docs))
cats = flatten_docs[::2]
texts = flatten_docs[1::2]
return cats, texts
train_cats, train_texts = read_f("train.middle.txt")
valid_cats, valid_texts = read_f("valid.middle.txt")
学習データの作成
from gensim import corpora
def c_mat(texts, dct):
tokens = [text.split(" ") for text in texts]
corpus = [dct.doc2bow(token) for token in tokens]
NUM = len(dct)
def corpus_to_list(arr):
ll = [0] * NUM
for k, v in arr:
ll[k] = v
return ll
return [corpus_to_list(c) for c in corpus]
dct = corpora.Dictionary.load_from_text('train.feature.dict.txt')
train_X = c_mat(train_texts, dct)
valid_X = c_mat(valid_texts, dct)
cat_d = {"b" : 1, "e" : 2, "m" : 3, "t" : 4}
train_Y = [cat_d[e] if cat_d.get(e) else -1 for e in train_cats]
valid_Y = [cat_d[e] if cat_d.get(e) else -1 for e in valid_cats]
print(*map(len, [train_X, train_Y, valid_X, valid_Y])) #> 10684 10684 1336 1336
フィッティング
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression(max_iter=1000)
lr.fit(train_X, train_Y)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, l1_ratio=None, max_iter=1000,
multi_class='auto', n_jobs=None, penalty='l2',
random_state=None, solver='lbfgs', tol=0.0001, verbose=0,
warm_start=False)
参考: https://qiita.com/nymwa/items/774ca6c542c1eaff160d
53. 予測
52 で学習したロジスティック回帰モデルを用い,与えられた記事見出しからカテゴリとその予測確率を計算するプログラムを実装せよ.
train_Y_pred = lr.predict(train_X)
valid_Y_pred = lr.predict(valid_X)
train_Y_prob = list(map(max, lr.predict_proba(train_X)))
valid_Y_prob = list(map(max, lr.predict_proba(valid_X)))
予測カテゴリと確率の導出だけで許してください。
54. 正解率の計測
52 で学習したロジスティック回帰モデルの正解率を,学習データおよび評価データ上で計測せよ.
from sklearn.metrics import accuracy_score
print("訓練データ")
print('accuracy = ', accuracy_score(y_true=train_Y, y_pred=train_Y_pred))
print()
print("検証データ")
print('accuracy = ', accuracy_score(y_true=valid_Y, y_pred=valid_Y_pred))
訓練データ
accuracy = 0.9873642830400599
検証データ
accuracy = 0.8959580838323353