<jupyter_start><jupyter_text>fastTextを用いたテキスト分類このノートブックでは、fastTextライブラリを使ってテキスト分類をする方法を紹介します。データセットとしては、dbpediaを使います。以下のリンクからダウンロードできます。- [dbpedia_csv.tar.gz](https://github.com/le-scientifique/torchDatasets/raw/master/dbpedia_csv.tar.gz)今回使うfastTextは単語埋め込みの学習とテキスト分類をするためのライブラリで、Facebook AI Research(FAIR)が作成しました。教師なしまたは教師ありで単語のベクトル表現を学習できます。以下のページから、157言語に対する事前学習済みモデルをダウンロードできます。- [Word vectors for 157 languages](https://fasttext.cc/docs/en/crawl-vectors.html)  準備 パッケージのインストール<jupyter_code>!pip -q install fasttext==0.9.2<jupyter_output>[?25l
[K     |████▊                           | 10 kB 13.6 MB/s eta 0:00:01
[K     |█████████▌                      | 20 kB 15.8 MB/s eta 0:00:01
[K     |██████████████▎                 | 30 kB 17.5 MB/s eta 0:00:01
[K     |███████████████████             | 40 kB 16.5 MB/s eta 0:00:01
[K     |███████████████████████▉        | 51 kB 9.1 MB/s eta 0:00:01
[K     |████████████████████████████▋   | 61 kB 7.6 MB/s eta 0:00:01
[K     |████████████████████████████████| 68 kB 2.7 MB/s 
[?25h  Building wheel for fasttext (setup.py) ... [?25l[?25hdone<jupyter_text>インポート<jupyter_code>import pandas as pd
from fasttext import train_supervised<jupyter_output><empty_output><jupyter_text>データセットのダウンロードまずは、データセットをダウンロードして展開します。<jupyter_code># データセットのダウンロード
!wget -P DATAPATH https://github.com/le-scientifique/torchDatasets/raw/master/dbpedia_csv.tar.gz

# 展開
!tar -xvf DATAPATH/dbpedia_csv.tar.gz -C DATAPATH

# フォルダ構造の確認
!ls -lah DATAPATH<jupyter_output>--2021-11-25 11:22:09--  https://github.com/le-scientifique/torchDatasets/raw/master/dbpedia_csv.tar.gz
Resolving github.com (github.com)... 172.16.58.3
Connecting to github.com (github.com)|172.16.58.3|:443... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://github.com/srhrshr/torchDatasets/raw/master/dbpedia_csv.tar.gz [following]
--2021-11-25 11:22:09--  https://github.com/srhrshr/torchDatasets/raw/master/dbpedia_csv.tar.gz
Reusing existing connection to github.com:443.
HTTP request sent, awaiting response... 302 Found
Location: https://raw.githubusercontent.com/srhrshr/torchDatasets/master/dbpedia_csv.tar.gz [following]
--2021-11-25 11:22:09--  https://raw.githubusercontent.com/srhrshr/torchDatasets/master/dbpedia_csv.tar.gz
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 172.16.58.3, 172.16.31.10, 192.168.127.12, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|172.16.58.3|:443...[...]<jupyter_text>データセットの読み込みデータセットを展開したら、pandasを使って読み込みます。<jupyter_code>data_path = 'DATAPATH'

# 学習データの読み込み
train_file = data_path + '/dbpedia_csv/train.csv'
df = pd.read_csv(train_file, header=None, names=['class', 'name', 'description'])

# テストデータの読み込み
test_file = data_path + '/dbpedia_csv/test.csv'
df_test = pd.read_csv(test_file, header=None, names=['class', 'name', 'description'])

# データ量の確認
print("Train:{} Test:{}".format(df.shape, df_test.shape))<jupyter_output>Train:(560000, 3) Test:(70000, 3)<jupyter_text>前処理<jupyter_code># クラスに関する手がかりがないのでマッピングを作成
# クラスを表す数値からクラス名へのマッピング
class_dict={
    1: 'Company',
    2: 'EducationalInstitution',
    3: 'Artist',
    4: 'Athlete',
    5: 'OfficeHolder',
    6: 'MeanOfTransportation',
    7: 'Building',
    8: 'NaturalPlace',
    9: 'Village',
    10: 'Animal',
    11: 'Plant',
    12: 'Album',
    13: 'Film',
    14: 'WrittenWork'
}

# 数値からクラス名への変換
df['class_name'] = df['class'].map(class_dict)
df.head()
df["class_name"].value_counts()<jupyter_output><empty_output><jupyter_text>次にテキストのクリーニングをします。ここでは主に以下の2つを行います。- 文字の置き換え- 正規化さらに、ラベルをfastTextの形式（`__class__ラベル名`）に変換します。<jupyter_code>def clean_it(text, normalize=True):
    # 文字の置き換え
    s = str(text)\
            .replace(',', ' ')\
            .replace('"', '')\
            .replace('\'', ' \' ')\
            .replace('.', ' . ')\
            .replace('(', ' ( ')\
            .replace(')', ' ) ')\
            .replace('!', ' ! ')\
            .replace('?', ' ? ')\
            .replace(':', ' ')\
            .replace(';', ' ')\
            .lower()
    
    # 正規化とテキストのエンコーディング
    if normalize:
        s = s.normalize('NFKD').str.encode('ascii', 'ignore').str.decode('utf-8')
    
    return s


def clean_df(data, cleanit=False, shuffleit=False, encodeit=False, label_prefix='__class__'):
    # 新しいデータを用意
    df = data[['name','description']].copy(deep=True)
    df['class'] = label_prefix + data['class'].astype(str) + ' '
    
    # 用意したデータをクリーニング
    if cleanit:
        df['name'] = df['name'].apply(lambda x: clean_it(x, encodeit))
        df['description'] = df['description'].apply(lambda x: clean_it(x, encodeit))
    
    # シャッフル
    if shuffleit:
        df.sample(frac=1).reset_index(drop=True)
            
    return df<jupyter_output><empty_output><jupyter_text>定義した関数を使って、データセットをきれいにしましょう。<jupyter_code>%%time
df_train_cleaned = clean_df(df, True, True)
df_test_cleaned = clean_df(df_test, True, True)
df_train_cleaned = df_train_cleaned.sample(frac=1, random_state=0)
df_test_cleaned = df_test_cleaned.sample(frac=1, random_state=0)<jupyter_output><empty_output><jupyter_text>クリーニングを終えたら、ディスクへ書き込みます。書き込んだデータは、後ほどfastTextの分類器から利用します。<jupyter_code># Write files to disk as fastText classifier API reads files from disk.
train_file = data_path + '/dbpedia_train.csv'
test_file = data_path + '/dbpedia_test.csv'

columns = ['class', 'name', 'description']
df_train_cleaned.to_csv(
    train_file,
    header=None,
    index=False,
    columns=columns
)
df_test_cleaned.to_csv(
    test_file,
    header=None,
    index=False,
    columns=columns
)
!head DATAPATH/dbpedia_train.csv<jupyter_output>__class__7 ,waterways experiment station, the waterways experiment station also known as wes-original cantonment in vicksburg mississippi is a sprawling 673-acre  ( 272 ha )  complex built in 1930 as an united states army corps of engineers research facility .  its campus is the site of the headquarters of the engineer research and development center  ( erdc )  of the corps of engineers .  wes is the largest of the four corps of engineers '  research and development laboratories .  the facility was listed on the u . s . 
__class__7 ,ibm building  ( seattle ) , the ibm building is a 20-story skyscraper in the metropolitan tract at 1200 fifth avenue seattle washington .  the building was designed by minoru yamasaki who also was architect of rainier tower on the corner diagonally opposite and the world trade center in new york city . 
__class__6 ,royal enfield fury, the royal enfield fury is a british motorcycle made by royal enfield at their factory in redditch .  the fury name has also [...]<jupyter_text>モデルの学習と評価 データを用意できたので、分類器を学習しましょう。fastTextには、学習用のファイル（csv）やラベル名の前にプレフィックスを指定する必要があります。プレフィックスは、デフォルトでは`__label__`ですが、今回は`__class__`を用いています。その他のパラメータについて知りたい場合、以下のページを参照してください。- [train_supervised parameters](https://github.com/facebookresearch/fastText/tree/master/pythontrain_supervised-parameters)<jupyter_code>%%time
model = train_supervised(
    input=train_file,
    label="__class__",
    lr=0.1,
    dim=10,
    epoch=5,
    wordNgrams=2,
    seed=0
)
for k in range(1,6):
    results = model.test(test_file, k=k)
    print(f"Test Samples: {results[0]} Precision@{k} : {results[1]*100:2.4f} Recall@{k} : {results[2]*100:2.4f}")<jupyter_output>Test Samples: 70000 Precision@1 : 98.4529 Recall@1 : 98.4529
Test Samples: 70000 Precision@2 : 49.8171 Recall@2 : 99.6343
Test Samples: 70000 Precision@3 : 33.2819 Recall@3 : 99.8457
Test Samples: 70000 Precision@4 : 24.9754 Recall@4 : 99.9014
Test Samples: 70000 Precision@5 : 19.9871 Recall@5 : 99.9357<jupyter_text>このデータセットをLogisticRegressionなどで学習してみると、fastTextがいかに優れているかがわかります。打ち負かすのはなかなか難しいでしょう。 `save_model`メソッドでモデルを保存できます。<jupyter_code>model.save_model("model.bin")
!ls -lh model.bin<jupyter_output>-rw-r--r-- 1 root root 140M Nov 25 13:39 model.bin<jupyter_text>量子化によるモデル容量の削減<jupyter_code># 学習したモデルのquantizeメソッドを呼ぶ
model.quantize(input=train_file, retrain=True)
# 結果の表示
print(model.test(test_file))
# モデルの保存
model.save_model("model.ftz")
!ls -lh model.ftz<jupyter_output>-rw-r--r-- 1 root root 36M Nov 25 13:40 model.ftz<jupyter_text>参考資料- [fastText/classification-example.sh](https://github.com/facebookresearch/fastText/blob/a20c0d27cd0ee88a25ea0433b7f03038cd728459/classification-example.sh)- [Supervised models](https://fasttext.cc/docs/en/supervised-models.html)- [fasttext 0.9.2](https://pypi.org/project/fasttext/)<jupyter_code><jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1508274231678487, 'fraction_numerical': 0.048581560283687944, 'mean_word_length': 4.508463541666667, 'pattern_counts': {'":': 0, '<': 42, '<?xml version=': 0, '>': 42, 'https://': 12, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 6, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '12', 'dir': 'jupyter-structured-clean-dedup', 'id': '448267', 'license': 'MIT', 'n_tokens_mistral': 4108, 'n_tokens_neox': 3672, 'n_words': 561, 'path': 'ch04/04_FastText_Example.ipynb', 'repo_name': 'oreilly-japan/practical-nlp-ja', 'size': '8478'}