言語処理100本ノック 2020 (Rev2)の「第6章: 機械学習」の50本目「データの入手・整形」記録です。
多分「第6章: 機械学習」は52以降をやりませんが、「第8章: ニューラルネット」をやりたかったため、前提となるこのノックに取り組みました。カンニングもしているので、コーディング自体は30分程度で終わっていますが、久々に100本ノック関連の記事書いたり、Google Colaboratory やGitHubを久々に使ったりとしていたら3時間くらいかかってしましました。ノック自体は非常に簡単で、ただの準備運動です。
記事「まとめ: 言語処理100本ノックで学べることと成果」に言語処理100本ノック 2015についてはまとめていますが、追加で差分の言語処理100本ノック 2020 (Rev2)についても更新します。
参考リンク
リンク | 備考 |
---|---|
050.データの入手・整形.ipynb | 回答プログラムのGitHubリンク |
Qiita記事 | 多くのソース部分のコピペ元 |
まとめ: 言語処理100本ノックで学べることと成果 | 言語処理100本ノックまとめ記事 |
環境
後々GPUを使わないと厳しいので、Goolge Colaboratory使いました。Pythonやそのパッケージでより新しいバージョンありますが、新機能使っていないので、プリインストールされているものをそのまま使っています。
種類 | バージョン | 内容 |
---|---|---|
Python | 3.7.11 | Google Colaboratoryのバージョン |
2.0.3 | Google Colaboratoryで提供されていたもの | |
pandas | 1.1.5 | Google Colaboratoryで提供されていたもの |
scikit-learn | 0.22.2 | Google Colaboratoryで提供されていたもの |
第6章: 機械学習
学習内容
『文書分類器を機械学習で構築します.さらに,機械学習手法の評価方法を学びます.
ノック内容
本章では,Fabio Gasparetti氏が公開しているNews Aggregator Data Setを用い,ニュース記事の見出しを「ビジネス」「科学技術」「エンターテイメント」「健康」のカテゴリに分類するタスク(カテゴリ分類)に取り組む.
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で再利用する).
学習データと評価データを作成したら,各カテゴリの事例数を確認せよ.
回答
回答結果
ノックの本題である「各カテゴリの事例数」です。一応、データセットごとの数値も出力しています。
略字の説明です。
b = business, t = science and technology, e = entertainment, m = health
--total--
e 152828
b 115967
t 108503
m 45639
Name: category, dtype: int64
--train--
b 4501
e 4235
t 1220
m 728
Name: category, dtype: int64
--valid--
b 563
e 529
t 153
m 91
Name: category, dtype: int64
--test--
b 563
e 530
t 152
m 91
Name: category, dtype: int64
回答プログラム 050.データの入手・整形.ipynb
GitHubには確認用コードも含めていますが、ここには必要なものだけ載せています。
from google.colab import drive
import pandas as pd
from sklearn.model_selection import train_test_split
drive.mount('/content/drive')
BASE_PATH = '/content/drive/MyDrive/ColabNotebooks/ML/NLP100_2020/06.MachineLearning'
# quotingをデフォルトの0にすると、ダブルコーテーションがTITLEの文字列先頭にあったときに、変な分割をしてしまう
df = pd.read_csv(BASE_PATH + '/input/newsCorpora.csv',
header=None, sep='\t', usecols=[0, 1, 3, 4], index_col=0,
names=['id', 'title', 'publisher', 'category'], quoting=3)
df = df.loc[df['publisher'].isin(['Reuters', 'Huffington Post', 'Businessweek', 'Contactmusic.com', 'Daily Mail']), ['title', 'category']]
train, valid_test = train_test_split(df, test_size=0.2, random_state=123, stratify=df['category'])
valid, test = train_test_split(valid_test, test_size=0.5, random_state=123, stratify=valid_test['category'])
# データの保存
train.to_csv(BASE_PATH + '/train.txt', sep='\t', index=False)
valid.to_csv(BASE_PATH + '/valid.txt', sep='\t', index=False)
test.to_csv(BASE_PATH + '/test.txt', sep='\t', index=False)
# 事例数の確認
print('--total--')
print(df['category'].value_counts())
print('--train--')
print(train['category'].value_counts())
print('--valida--')
print(valid['category'].value_counts())
print('--test--')
print(test['category'].value_counts())
回答解説
いくつか回答の補足説明です。
データ確認
News Aggregator Data Setを確認します。
対象のファイルは"newsCorpora.csv"
で以下の列があるようです。
No. | 列名 | 使用 | 内容 |
---|---|---|---|
0 | ID | Indexとして使用 | Numeric ID |
1 | TITLE | 使用(説明変数) | News title |
2 | URL | 不使用 | Url |
3 | PUBLISHER | 使用(フィルタ用) | Publisher name |
4 | CATEGORY | 使用(目的変数) | News category (b = business, t = science and technology, e = entertainment, m = health) |
5 | STORY | 不使用 | Alphanumeric ID of the cluster that includes news about the same story |
6 | HOSTNAME | 不使用 | Url hostname |
7 | TIMESTAMP | 不使用 | Approximate time the news was published, as the number of milliseconds since the epoch 00:00:00 GMT, January 1, 1970 |
少しわかりにくかったですが、以下のリンクからデータをダウンロードし、zip内の"newsCorpora.csv"
を使います。
データ読込
pandasでデータ読込します。
不要な列は読み込みません。TITLE
とPUBLISHER
とCATEGORY
以外は使わないようなので読込しません。ID
はインデックス列として使いました。
TITLE
にはダブルコーテーションで始まる文字列があるようで、その場合、意図しないエスケープ処理が行われるためquoting=3
として、ダブルコーテーションによるエスケープ処理を無効化しました。
# quotingをデフォルトの0にすると、ダブルコーテーションがTITLEの文字列先頭にあったときに、変な分割をしてしまう
df = pd.read_csv(BASE_PATH + '/input/newsCorpora.csv',
header=None, sep='\t', usecols=[0, 1, 3, 4], index_col=0,
names=['id', 'title', 'publisher', 'category'], quoting=3)
データ数確認
info
でデータ数を確認します。News Aggregator Data Setに書かれている以下の数値と一致します。
422937 news pages and divided up into:
df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 422937 entries, 1 to 422937
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 title 422937 non-null object
1 publisher 422935 non-null object
2 category 422937 non-null object
dtypes: object(3)
memory usage: 12.9+ MB
データ内容確認
そのままDataFrameの内容を確認。
print(df)
title ... category
id ...
1 Fed official says weak data caused by weather,... ... b
2 Fed's Charles Plosser sees high bar for change... ... b
3 US open: Stocks fall after Fed official hints ... ... b
4 Fed risks falling 'behind the curve', Charles ... ... b
5 Fed's Plosser: Nasty Weather Has Curbed Job Gr... ... b
... ... ... ...
422933 Surgeons to remove 4-year-old's rib to rebuild... ... m
422934 Boy to have surgery on esophagus after battery... ... m
422935 Child who swallowed battery to have reconstruc... ... m
422936 Phoenix boy undergoes surgery to repair throat... ... m
422937 Phoenix boy undergoes surgery to repair throat... ... m
[422937 rows x 3 columns]
フィルタ
publisher
でフィルタして、列publisher
をドロップします。
df = df.loc[df['publisher'].isin(['Reuters', 'Huffington Post', 'Businessweek', 'Contactmusic.com', 'Daily Mail']), ['title', 'category']]
df.info()
データが1/30くらいまで少なくなりました。
<class 'pandas.core.frame.DataFrame'>
Int64Index: 13356 entries, 13 to 422838
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 title 13356 non-null object
1 category 13356 non-null object
dtypes: object(2)
memory usage: 313.0+ KB
データ分割
おなじみtrain_test_split
関数を使って分割します。
再現性があるように適当な数値でrandom_state固定しておきます。stratify
を指定すると、指定列の件数を分割時に考慮してくれるようです(よく確認していないけど合っているはず)。
3分割を一気にできないか関数を探しましたが、fast-ml
パッケージでGitHubにStar数が5しかないものしか見つからなかったので諦めました。
train, valid_test = train_test_split(df, test_size=0.2, random_state=123, stratify=df['category'])
valid, test = train_test_split(valid_test, test_size=0.5, random_state=123, stratify=valid_test['category'])