はじめに
確定申告の時期になり、会計データを再確認すると勘定科目の間違いがちらほら見つかるので、機械学習で会計データを学習させて、勘定科目を予測できると良いのではないかと思い試してみました。
会計データの読込
会計ソフトからCSV形式でデータをエクスポートし、それを使います。
ちなみに、今回のデータは「JDL IBEX出納帳」というソフトのものを例に使用しています。
以下のコードでデータを読み込みます。
import pandas as pd
filename = "JDL出納帳-xxxx-xxxx-仕訳.csv"
df = pd.read_csv(filename, encoding="Shift-JIS", skiprows=3)
読み込んだデータから使用するデータを絞り込む。
ここでは、摘要と借方科目のコードと名称を使用します。
columns = ["摘要", "借方科目", "借方科目正式名称"]
df_counts = df[columns].dropna()
形態素解析
摘要のデータについて形態素解析により、文字データを指向性のある数値としてベクトル化します。
形態素解析にはJanomeというライブラリを使用します。
もし、インストールされていなければ、以下のコマンドでインストールしておく必要があります。
$ pip install janome
以下のコードで、摘要のデータをトークンにわけたデータに変換します。
from janome.tokenizer import Tokenizer
t = Tokenizer()
notes = []
for ix in df_counts.index:
note = df_counts.ix[ix,"摘要"]
tokens = t.tokenize(note.replace(' ',' '))
words = ""
for token in tokens:
words += " " + token.surface
notes.append(words.replace(' \u3000', ''))
この結果、以下のような変換が行われ、単語毎に半角スペースを入れた文字列になります。
元の摘要データ 「手土産代 BLUESKY羽田」
変化後のデータ 「手土産 代 BLUESKY 羽田」
この文字列を以下のコードでベクトル化し、入力データとして使用します。
from sklearn.feature_extraction.text import TfidfVectorizer
vect = TfidfVectorizer()
vect.fit(notes)
X = vect.transform(notes)
また、教師データとして勘定科目のコードを使用します。
y = df_counts.借方科目.as_matrix().astype("int").flatten()
機械学習
数値に変換したデータをクロスバリデーションで学習データと検証データに分割します。
from sklearn import cross_validation
test_size = 0.2
X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=test_size)
分割したデータを使って学習します。
ここではモデルとしてLinearSVCを使用します。
from sklearn.svm import LinearSVC
clf = LinearSVC(C=120.0, random_state=42)
clf.fit(X_train, y_train)
clf.score(X_test, y_test)
スコアは「0.89932885906040272」でした。
予測
学習した結果から、以下のようにテストデータを入力して、どんな勘定科目が予測されるか確認してみます。
tests = [
"高速道路利用料",
"パソコン部品代",
"切手代"
]
notes = []
for note in tests:
tokens = t.tokenize(note)
words = ""
for token in tokens:
words += " " + token.surface
notes.append(words)
X = vect.transform(notes)
result = clf.predict(X)
df_rs = df_counts[["借方科目正式名称", "借方科目"]]
df_rs.index = df_counts["借方科目"].astype("int")
df_rs = df_rs[~df_rs.index.duplicated()]["借方科目正式名称"]
for i in range(len(tests)):
print(tests[i], "\t[",df_rs.ix[result[i]], "]")
出力結果は...
高速道路利用料 [ 旅費交通 ]
パソコン部品代 [ 消耗品費 ]
切手代 [ 通信費 ]
かなり良い感じ(^-^)
ちなみに振替伝票はもう少し工夫が必要ですね。
他にも月や曜日、決算の情報とかも利用できるともっと良いかなと思ったのですが、学習データの扱い方がいまいちわからなかったので、また後日調べようと思います。
さて、次は何しようかな。
(おまけ)学習と予測の切り離し
上述のプログラムをそのまま使おうとすると、実行するたびに実データを読込、学習してから予測するため、効率的とは言えません。
そこで、学習データを保存し、予測する部分では学習済データを読み込んで予測するように変更してみます。
学習結果の保存
前述のプログラムの最後に以下のようなコードを追加すると、学習データを保存することができます。
from sklearn.externals import joblib
joblib.dump(vect, 'data/vect.pkl')
joblib.dump(clf, 'data/clf.pkl')
df_rs.to_csv("data/code.csv")
学習結果の読込
新しいプログラムで、以下のように学習データを読み込みます。
import pandas as pd
filename = "data/code.csv"
df = pd.read_csv(filename, header=None)
df.index = df.pop(0)
df_rs = df.pop(1)
from sklearn.externals import joblib
clf = joblib.load('data/clf.pkl')
vect = joblib.load('data/vect.pkl')
予測
学習データを読み込んだら、続けて予測を実行します。
from janome.tokenizer import Tokenizer
t = Tokenizer()
tests = [
"高速道路利用料",
"パソコン部品代",
"切手代",
]
notes = []
for note in tests:
tokens = t.tokenize(note)
words = ""
for token in tokens:
words += " " + token.surface
notes.append(words)
X = vect.transform(notes)
result = clf.predict(X)
for i in range(len(tests)):
print(tests[i], "\t[",df_rs.loc[result[i]], "]")
実行結果は...
高速道路利用料 [ 旅費交通 ]
パソコン部品代 [ 消耗品費 ]
切手代 [ 通信費 ]
できた!