bertモデルを使ってlivedoorニュースの分類をする

初めに

色々LLMを触ってきて、ちゃんと?bertを触ったことがないので以下の記事を自分でもやってみます.
一部記事の内容から変更して実行しています

soroban.highreso.jp

以下でColobは公開しています

colab.research.google.com

また作成したモデルは以下で公開しています

huggingface.co

環境

準備

ライブラリのインストール

!pip install transformers==4.28.0
!pip install nlp
!pip install datasets
!pip install fugashi
!pip install ipadic
!pip install sentencepiece
!pip install scikit-learn
!pip install tensorboard

データの取得と整理

!wget "https://www.rondhuit.com/download/ldcc-20140209.tar.gz"

# ファイルを解凍し、カテゴリー数と内容を確認
import tarfile
import os
# 解凍
tar = tarfile.open("ldcc-20140209.tar.gz", "r:gz")
tar.extractall("./data/livedoor/")
tar.close()
# ディレクトリのファイルとディレクトリを確認
files_folders = [name for name in os.listdir("./data/livedoor/text/")]
print(files_folders)
# カテゴリーのディレクトリのみを抽出
categories = [name for name in os.listdir(
    "./data/livedoor/text/") if os.path.isdir("./data/livedoor/text/"+name)]
print("カテゴリー数:", len(categories))
print(categories)

import glob  # ファイルの取得に使用
import os
path = "./data/livedoor/text/"  # ディレクトリの場所を指定
dir_files = os.listdir(path=path)#指定したpathのファイルとディレクトリの一覧を取得
dirs = [f for f in dir_files if os.path.isdir(os.path.join(path, f))]  # ディレクトリをリストとして取り出す
text_label_data = []  # 文章とラベルのセット
dir_count = 0  # ディレクトリ数のカウント
file_count= 0  # ファイル数のカウント
for i in range(len(dirs)):#ディレクトリの数だけループ処理
    dir = dirs[i]#ディレクトリの名前を取り出す
    files = glob.glob(path + dir + "/*.txt")  # ファイルの一覧
    dir_count += 1
    for file in files:
        if os.path.basename(file) == "LICENSE.txt":#LICENSE.txtは除外する(ループをスキップ)
            continue
        with open(file, "r") as f:#ファイルを開く
            text = f.readlines()[3:]#指定の行だけ読み込む 4行目以降を読み込む
            text = "".join(text)#リストなのでjoinで結合する 空の文字列に結合して一つの文字列にする
            text = text.translate(str.maketrans({"\n":"", "\t":"", "\r":"", "\u3000":""})) #不要な文字を除去する
            text_label_data.append([text, i])#本文とディレクトリ番号をリストに加える
        file_count += 1
        print("\rfiles: " + str(file_count) +" "+ "dirs: " + str(dir_count), end="")

import csv
from sklearn.model_selection import train_test_split
news_train, news_test =  train_test_split(text_label_data, shuffle=True)  # 学習用とテスト用に分割
news_path = "./data/"
with open(news_path+"news_train.csv", "w") as f:#学習データ
    writer = csv.writer(f)#writerを作る
    writer.writerows(news_train)#csvファイルとして書き込み
with open(news_path+"news_test.csv", "w") as f:#テストデータ
    writer = csv.writer(f)
    writer.writerows(news_test)

モデルの学習

from transformers import BertForSequenceClassification, BertJapaneseTokenizer
sc_model = BertForSequenceClassification.from_pretrained("cl-tohoku/bert-base-japanese-whole-word-masking", num_labels=9)#事前学習済みBERTモデルの読み込み
sc_model.cuda()#GPU対応
tokenizer = BertJapaneseTokenizer.from_pretrained("cl-tohoku/bert-base-japanese-whole-word-masking")#事前学習済みのBERTトークナイザーを読み込み

from datasets import load_dataset
def tokenize(batch):
    return tokenizer(batch["text"], padding=True, truncation=True, max_length=512) #受け取ったバッチからtextデータを取り出してtokenizerに入れる
news_path = "./data/"
train_data = load_dataset("csv", data_files=news_path+"news_train.csv",column_names=["text", "label"], split="train")#CSVデータの読み込み #カラムの指定,#学習データとテストデータは事前に分割済みのためtrainを指定
train_data = train_data.map(tokenize, batched=True,batch_size=len(train_data))#単語ごとに分割する  #バッチサイズは学習データすべてを指定
train_data.set_format("torch", columns=["input_ids", "label"])#学習データのフォーマット指定,Pytorchを指定,input_idsとlabelのカラムを指定
test_data = load_dataset("csv", data_files=news_path+"news_test.csv", column_names=["text", "label"], split="train")#学習データとテストデータは事前に分割済みのためtrainを指定
test_data = test_data.map(tokenize, batched=True, batch_size=len(test_data))
test_data.set_format("torch", columns=["input_ids", "label"])

from sklearn.metrics import accuracy_score
def compute_metrics(result):
    labels = result.label_ids
    preds = result.predictions.argmax(-1)
    acc = accuracy_score(labels, preds)
    return {
        "accuracy": acc,
    }

from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
    output_dir = "./data/results",
    num_train_epochs = 2,
    per_device_train_batch_size = 16,
    per_device_eval_batch_size = 32,
    warmup_steps = 500,  # 学習係数が0からこのステップ数で上昇
    weight_decay = 0.01,  # 重みの減衰率
    logging_dir = "./data/logs",
    evaluation_strategy = "steps"
)
trainer = Trainer(
    model = sc_model,
    args = training_args,
    compute_metrics = compute_metrics,
    train_dataset = train_data,
    eval_dataset = test_data,
)

trainer.train()

モデルの評価

trainer.evaluate()

lossは以下のようになっています

モデルの保存とhuggingfaceへのアップロード

以下でモデルをローカルに保存します

news_path = "./data/"
sc_model.save_pretrained(news_path)
tokenizer.save_pretrained(news_path)

以下で /data ディレクトリの中をhuggingfaceにアップロードします

!huggingface-cli upload ayousanz/bert-livedoor-news-category data .

追加学習をしたモデルを使って分類

import glob  # ファイルの取得に使用
import os
import torch
import random
category_list = ["dokujo-tsushin", "it-life-hack", "livedoor-homme","kaden-channel", "movie-enter", "sports-watch","smax","topic-news","peachy"]
category = random.choice(category_list)
#print("dirname:", category)
random_number = random.randrange(1, 99)
#print(random_number)
sample_path = "./data/livedoor/text/"  # ディレクトリの場所を指定
files = glob.glob(sample_path + category + "/*.txt")  # ファイルの一覧
file = files[random_number]  # 適当なニュースを1つ取り出した
file_name = os.path.basename(file)
#print("filename:", file_name)
dir_files = os.listdir(path=sample_path)
dirs = [f for f in dir_files if os.path.isdir(os.path.join(sample_path, f))]  # ディレクトリ一覧
with open(file, "r") as f:
    sample_text = f.readlines()[3:]
    sample_text = "".join(sample_text)
    sample_text = sample_text.translate(str.maketrans({"\n":"", "\t":"", "\r":"", "\u3000":""})) 
print(sample_text)
max_length = 512
words = loaded_tokenizer.tokenize(sample_text) #torknizeを行う
word_ids = loaded_tokenizer.convert_tokens_to_ids(words)  # 単語をインデックスに変換
word_tensor = torch.tensor([word_ids[:max_length]])  # テンソルに変換 スライスを使って単語が512より多い場合は切る
x = word_tensor.cuda()  # GPU対応
y = loaded_model(x)  # 予測
pred = y[0].argmax(-1)  # 最大値のインデックス
print("predict-result:", dirs[pred])

こちらを実行すると以下のように分類してくれます

オンラインゲーム(いわゆるネトゲ)は、インターネットに繋がっているため様々な問題が発生する。不正対象となるオンラインゲーム上での経済活動として話題になるのがRMTだ。RMTは「リアルマネートレード」の略で、ゲーム内の通貨やアイテムなどを、実際のお金でユーザー同士が売買するというものだ。長時間かけないと取れないレアアイテムなどは、数千〜数万円で取り引きされ、レアアイテムだけを狙う輩がPOP場所にずっと張り付き占有してしまうことでゲームバランスが大きく崩れてしまう原因にもなっている。このような悪質な行為はゲームの規約などで禁止されていることが多い。先日発売されたWii用ゲームのドラクエXでも当然禁止されているが、悲しいかなRMTでアイテムやゲーム内の通貨をやりとりしようとしている人もいるようだ。オンラインゲーム内にはアイテム交換などができる仕組みがある。これはゲーム内で仲良くなったキャラクター同士のアイテム交換などが本来の目的だ。この仕組みを利用して、ゲーム外で実際のお金をやりとりし、アイテムやゲーム内の通貨売買を行うのがRMTだ。RMTはドラクエXのような同時に大人数が遊ぶMMORPG以外でも、主にスマートフォンなどで遊ぶソーシャルゲームと言われる物でも問題になっている。ゲームにアイテム交換などの仕組みがある限り、ゲーム外での金銭の授受を技術的に防ぐことは難しい。■それなりに稼げるゲームの規約などともかく、RMTをよく思わないユーザーも少なくないが、規約違反であろうが手っ取り早くゲームを攻略できるようになるならRMTを使うのもよしとするユーザーもいる。悪いことをしても、ばれなきゃOKという間違った考えに基づいているわけだ。例えば、ドラクエXではゲーム内の通貨「ゴールド」を貯めるのに時間がかかる。今までのドラクエシリーズのようにモンスターを倒して集めていては時間がかかりすぎるが、ゲーム内で職人となり武器や道具などを生産してゲーム内で販売することで、ゴールドもそれなりに貯まるようになっている。この仕組みをよく知らなかったり、面倒だと思うと、ネット上でRMT業者がゴールドを売ってたら買いたくなる人もいるだろう。ある程度、この仕組みなどをフル活用してゴールドを効率的に稼げるようになったら、業者にゴールドを買い取ってもらいたい人もいるかもしれない。例えば、1時間で2000ゴールド増やせるとして、それを1000円で販売できれば、普通にアルバイトなどをするのと同じくらい稼げることになる。個人が、アルバイト感覚でやるなら規模も小さいが、業者がプログラムを操作して、自動的にそのようなことを行う場合もある。これをBOTなどというが、Wii用のドラクエXの場合、ゲーム専用機ということで難しいが、PC用のゲームでは実際にプログラムを操作したり、マクロを組んだりして手動だと割に合わないことを複数のアカウントを使い大規模に行っていることもある。当然ながら、このような行為が運営側に発覚した場合、アカウント剥奪などの処分を受けることになる。RMT行為はほとんどのオンラインゲームでも原則禁止されている行為である。この問題はどのオンラインゲームにも共通して存在しているが、ユーザーが多ければ多いほど多くの問題が発生する可能性がある。世界中から接続可能なオンラインゲームの場合、日本人向けのサーバーにRMT目的の外国人が大量に接続して乱獲しまくることで通常のプレイに支障が出ることがある。特に中華圏からの接続が多いみたいだ。彼らにとっての数万円は日本人にとっての数十万円に匹敵する価値があるので、それこそ死に物狂いで狩りを行う。生活がかかっているから何でもアリ、お構いなしだ。BANされるまでひたすら周囲に迷惑をかけまくり、BANされたら新アカウントを作ってまたやってくる。これを防ぐには、運営側の対処も重要だが、ユーザー同士がこうした行為をしないようにするのも重要だ。しかし、ドラクエXのようにオンラインゲームに慣れていないユーザーが多い場合はこれもまた難しい。結局はプレイヤーの良心に訴えるしかないというのが現状だ。こうしたRMT行為はいたちごっこで今後も続いて行くのだろう。上倉賢 @kamikura [digi2(デジ通)] digi2は「デジタル通」の略です。現在のデジタル機器は使いこなしが難しくなっています。皆さんがデジタル機器の「通」に近づくための情報を、皆さんよりすこし通な執筆陣が提供します。■デジ通の記事をもっと見る・iPhone 4Sの音声認識機能は使える 新しいiPadでも使える音声認識・英語の音声認識で英会話練習 自分の発音を客観的に知る方法・サービス終了後はどうなる!電子書籍は永遠に読めるのか・キンドルが日本に参入する?Amazonの電子書籍をおさらい・iPhoneが据え置きゲーム機も殺す?多機能化するスマートフォンドラゴンクエストX 目覚めし五つの種族 オンライン (Wii USBメモリー16GB同梱版) (封入特典:ゲーム内アイテムのモーモンのぼうし同梱)クチコミを見る
predict-result: it-life-hack