Qiitaの指定したタグの記事を教師なし学習により分類する

# はじめに
今回は、指定したタグの記事データに対して、教師なし学習（k-means法）による分類を行っていきます。

指定したタグの記事データを取得する方法については、こちらの記事を参考にしてください。

>・Qiita API を使って、記事データを取得する方法
https://qiita.com/wakudar/items/8c594c8cc7bda9b93b4e

# プログラムの流れ
1. 記事データの読み込みと分類（文章の分かち書き）
2. TF-IDFによる記事データのベクトル化
3. k-means法によるクラスタリング
4. 結果の可視化
5. 分類結果の多数派を見つけ、誤分類した記事データの出力

# 1. 記事データの読み込みと分類
```python
# 分かち書き
def wakatigaki(text):
    mecab = MeCab.Tagger()
    mecab_result = mecab.parse(text).replace("EOS", "").split('\n')
    mecab_result = [i.replace("#", "").replace("\"", "").replace("\'", "").replace("\t", "_").replace(",","_").split("_") for i in mecab_result if i != ""]
    return mecab_result

# 記事データの読み込みと分類
def load_article():
    category = ["Vagrant", "iOS", "numpy"]
    category_num = [0, 1, 2]
    docs  = []
    labels = []
    labels_num = []

    for c_name, c_num in zip(category, category_num):
        files = glob.glob("./qiita/{c_name}/*.txt".format(c_name=c_name))

        text = ""
        for file in files:
            with open(file, "r", encoding="utf-8") as f:
                lines = f.read().splitlines()
                body = "".join(lines[0:]).replace('\u3000', '')
                text = body
                text = " ".join([w[0] for w in wakatigaki(text)])

            docs.append(text)
            labels.append(c_name)
            labels_num.append(c_num)
    return docs, labels, category

# 記事データの読み込みと分類
docs, labels, category = load_article()
```

記事データは、qiita/タグ名/------.txtという形で保存しています。
今回は、予め保存しておいた「Vagrant」「iOS」「numpy」の３つのタグのカテゴリ推定を行います。

# 2. TF-IDFによる文章のベクトル化
```python
# TF-IDFによるベクトル表現変換器を生成
vectorizer = TfidfVectorizer()
# 文書のベクトル変換化
vecs = vectorizer.fit_transform(docs)
```

# 3. k-means法の実装
```python
# k-means法を実装
kmeans_model = KMeans(n_clusters=n_cluster, random_state=0).fit(vecs)
#クラスタリング結果のラベルを格納
predict_labels = kmeans_model.labels_
```

# 4. 結果の可視化
```python
# 結果を集計し可視化
res = {
    0:{},
    1:{},
    2:{}
}

# 結果の格納と表示
for pre_label, r_label in zip(predict_labels, labels):
    # 値がある場合の処理
    try:
        res[pre_label][r_label] += 1
    # 例外処理
    except:
        res[pre_label][r_label] = 1

# 結果の出力
for i in range(n_cluster):
    print(res[i])
```

# 5. 分類結果の多数派カテゴリを見つけ、誤分類した記事データを出力
### それぞれのラベルの多数派を見つける
```python
# 多数派のカテゴリ名 major_cat
major_cat = []
# 多数派のカテゴリ名の要素番号
major_num = []
for i in range(n_cluster):
    major_cat.append(max(res[i], key=res[i].get))
    major_num.append(category.index(major_cat[i]))
```

# 多数派に基づいたカテゴリのラベル adjusted_labels を生成
```python
adjusted_labels = []
# それぞれのカテゴリの記事数
article_num = [900, 900, 900]
for i in range(n_cluster):
    adjusted_labels.extend([major_num[i]] * article_num[i])
```

# ラベリング結果の変化の比較
```python
# txtファイルの名前用変数cnt
cnt = 0
# クラスタリング前と後のラベルが違うときは、その記事の内容を出力
for label1, label2 in zip(adjusted_labels, predict_labels):
    cnt += 1
    if label1 == label2:
        pass
    else:
        path_w = "./result/" + str(label1) + "-" + str(label2) + "/" + str(cnt) + ".txt"
        # ファイル名 path_w の出力
        with open(path_w, mode='w') as f:
            f.write(docs[cnt])
```

# 結果
```
{'Vagrant': 108, 'iOS': 900, 'numpy': 333}
{'Vagrant': 792}
{'numpy': 567}
```
それぞれのタグの正解率は、
iOS: 約67%
Vagrant: 約88%
numpy: 約63%
という結果になりました。

# 考察・まとめ
今回は、あまりいい精度ではいきませんでした...
Livedoorニュースコーパスに使ったのとほぼ同じようなプログラムで実行しているため、Qiitaに多いプログラムのソースコードの部分が影響しているのではないかと考えられます。
今後は、より精度を高めるために別の学習方法で分類してみるなどの方法を検討する必要があると思いますので、時間があるときにやってみたいと思います！

# その他 工夫した点
・今回は、タグ同士の重なりがないように複数のタグを検索して重なりが少なかった３つのタグを選んでいます。（Vagrant, iOS, numpy）

・AndroidとiOSのタグで一度分類を試してみましたが、散々な結果になりました。多分、スマホ開発という点で２つのタグをつけた記事が多かったんだと思います。

# 参考
・教師なし文章分類(文章クラスタリング)[python]
https://appswingby.com/2019/08/15/python%E6%95%99%E5%B8%AB%E3%81%AA%E3%81%97%E6%96%87%E7%AB%A0%E5%88%86%E9%A1%9E%EF%BC%88%E6%96%87%E7%AB%A0%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0%EF%BC%89/

・Qiitaのタグ一覧
https://qiita.com/tags

・タグ「iOS」とタグ「Vagrant」両方を持つ記事
https://qiita.com/search?q=tag%3A+iOS+tag%3AVagrant