Google AutoML Tables の機械学習を使ってカード利用明細から家計簿の勘定科目を予測する(5)モデルの評価と改良

教師付きの機械学習サービスである Google AutoML Tables を使って、クレジットカードの利用明細(CSVファイル)から家計簿の勘定科目を予測してみます。第5回目の本記事では、試行錯誤して感じたことをメモします。

【目次】

[1]はじめに


第2回から第4回の記事で、学習から予測まで、主にAutoML Tablesの使い方を中心に見てきました。使い方が分かればより精度よく予測するために、いろいろと試行錯誤したくなります。

そこで本記事では、試行錯誤したことや感想をメモします。とはいっても、無料トライアル枠内でやれた範囲であるため、試したことは限られますが…

ところで自分用の実験メモといった内容の記事なので、かなり冗長です。このため、[5]使ってみた印象だけお読みいただいたほうが良いかもしれません…


[2]トレーニングデータのテキストの扱い

第1回の記事で準備したトレーニングデータは、以下の4項目を持つCSVファイルでした。
  • 特徴
    • date(日付)
    • description(摘要)
    • amount(金額)
  • ターゲット
    • account(勘定科目)
      • 食費、医療費など、11クラスあります。

<CSVファイルのレイアウト例
date,description,amount,account
2019/03/01,病院名,1210,医療費
(以下同様)

以下では、このうち description(摘要)に関する試行錯誤とその実験結果をメモします。

(1)実験1:カテゴリ型

準備したトレーニングデータを AutoML Tables のデータセットにインポートすると、dateはタイムスタンプ型、amountは数値型、accountはカテゴリ型として、期待通り自動認識されました。

一方、description(摘要)はテキスト型と期待していたら、カテゴリ型として認識されていました。(テキスト型を選択することもできませんでした。)

具体的には、カテゴリ型の固有の値として239個が認識されており、AutoML Tables のトレーニングタブから詳細情報を見ることができます。

確認してみると、例えば携帯料金だと、「携帯会社名 4月分」、「携帯会社名 5月分」といったように同じ利用料金でも個別に12パターンが認識されていました。つまり異なる文字があれば関連のない別の値になっています。

さて、なぜテキストとして扱われないのかな?と思って、上記 AutoML Tables ドキュメントを調べてみると、以下のように説明されています。

<テキスト>
  • テキスト値は自由形式テキストを表し、通常はテキスト トークンで構成されます。
  • テキスト値の例を示します。
    • "The quick brown fox"
    • "This restaurant is the best! The food is delicious"
  • テキスト フィールドは、空白によってモデル トレーニングのトークンに解析されます。

また、以下のドキュメントにより詳細な説明があります。
  • モデルのトレーニング:スキーマの確認
  • 自由形式のテキストを含むフィールドは「テキスト」であること。
    • テキスト フィールドは UnicodeScriptTokenizer によりトークンに分離され、個々のトークンがモデルのトレーニングに使用されます。UnicodeScriptTokenizer はテキストを空白文字でトークン化し、句読点をテキストから分離し、異なる言語を互いに分離します。
  • 列の値が有限の値セットのいずれかである場合は、フィールドで使用されるデータの種類に関係なく、これはカテゴリになります。

実際のトレーニングデータのdescription(摘要)の値を見てみると、カード会社の利用履歴は「ロ―ソン 店名/決済方法」、「AMAZON.CO.JP」、「GOOGLE CLOUD SERVICE」のように、空白や区切り文字を含めて全て全角文字でした。

どうやら半角空白がないためトークン分割されないことから、テキスト型ではなくカテゴリ型として認識されるようです。

実際に試してみたところ、半角空白を含む(トークンが2つ以上ある)データが1つも無い場合は、カテゴリ型として認識されてテキスト型は選択できませんでした。

カード会社からダウンロードしたデータのままだとテキスト型として扱われないことは分かりましたが、実際にカード会社の利用履歴に書かれる内容はパターン化されているし、何も加工しない状態でどれくらい予測ができるのか興味もありました。

そこで、実験1として、description(摘要)をカテゴリ型(239個)のままトレーニングしました。結果は後述します。

(2)実験2:Unicode正規化

description(摘要)をカテゴリ型として扱うと、月毎の携帯会社の請求のように、ローソンで買い物でも店や決済方法が変わると関連のない別のクラスになってしまいます。自分の行動パターンは知れている(笑)とはいえ、将来に対してもう少し汎用性のあるモデルにしたいと思うと、やはりテキスト型も試してみたくなります。

そこで、テキスト型が選択できるように、トークンの分離が認識されるように摘要文字列を変換します。

今回の例では、全角空白を半角空白に変換するだけでもテキストとして認識されるとは思いますが、簡単かつもう少し汎用的な方法として、Unicode正規化を試してみます。

Unicodeの正規化についての説明は割愛しますが、今回はPythonで以下のような変換(NFKC)を行いました。
import unicodedata

def normalize_text(text):
  return  unicodedata.normalize('NFKC',text)

(変換例)
変換前:GOOGLE CLOUD/AMAZON.CO.JP@1212 アイアイ
変換後:GOOGLE CLOUD/AMAZON.CO.JP@1212 アイアイ

これで全角空白は半角空白に、全角英数字や記号は半角英数字記号に、半角カナは全角カナのように正規化(変換)されます。

今回対象としたカード会社の利用明細は全て全角文字で統一されていましたが、銀行では半角カナだったり、色々パターンがあるようなので、適用範囲を広げる際には単にトークン化するより良いと思って試してみることにしました。

ということで、元データのdescription(摘要)をUnicode正規化したものを実験2としてトレーニングしました。結果は後述します。

(3)実験3:さらにトークンを増やす

AutoML Tables のドキュメントに以下の情報があります。

これを読むと、テキストの与え方によってモデルが変わる可能性があることが分かります。
  • スペースを使ってテキストを区切る
    • 単語はスペースで区切られるため、他の文字で区切られている単語は 1 つのエンティティとして扱われます。
    • たとえば、テキスト「red/green/blue」を指定した場合、「red」、「green」、「blue」にトークン化されません。これらの個々の単語がモデルのトレーニングで重要になるかもしれない場合は、このテキストを「red green blue」に変換してからトレーニング データに含めてください。
  • ストップワードは特別な扱いを受けず、削除されません。

今回の例だと、ローソンの利用履歴は「ロ―ソン 店名/決済方法」と全角空白と全角スラッシュの区切りがありました。Unicode正規化によって全角空白は半角空白になるためトークン分割されますが、スラッシュは半角変換されてもトークン分割されないようです。

別の例で、ファミリーマートだと「フアミリ―マ―ト店名/決済方法」のように店名との区切りがありませんでした。本当は、スラッシュに限らず、会社名とそれ以降のような、意味的な区切り(半角空白)を入れたり、区切り文字を除去したりすると面白そうだと思いました。

しかし、今回はまずはトークンの数の違いがモデルに影響があるかを見てみたいと思い、実験2のUnicode正規化に加えて、スラッシュを半角空白に置き換えてトレーニングしてみます。(実際のデータを見ると、スラッシュの数もまとまった数がありましたので、半角空白で区切られるデータがかなり多くなりました。)

ということで、実験3として、実験2のUnicode正規化に加えてスラッシュを半角空白に変換してトレーニングしました。結果は後述します。

[3]実験結果

ここでは実験1~3のモデルを実際のデータで試したり、各モデルの評価指標を確認して改善できそうなことを考えます。

(1)翌月50件のデータで試してみる

今回のトレーニングデータは、2019年2月から2021年6月までのカード利用履歴をもとに作成しました。

モデルができたら実際に利用できるレベルかどうかを早く知りたくて(笑)、モデルの評価指標を検討する前に、トレーニングデータに使った期間の翌月の実際のカード利用履歴で試してみることにしました。

実験は以下のように行いました。
  1. 翌月(2021年7月)は50件の利用履歴(日付、摘要、金額)がありましたので、これから予測リクエストを作りました。
  2. 予測リクエストに対する正解の勘定科目を自分で別途作成しました。
  3. 学習済モデルをエクスポートして、ローカル環境のモデルサーバで、予測リクエスト(日付、摘要、金額)から勘定科目を予測しました。
    • ここでは最もスコアの高い勘定科目の予測値としました。
  4. 正解と予測値と照合しました。

以下は、翌月50件のデータに対する予測の正解数(率)です。
実験1 実験2 実験3
翌月50件の正解 50 (100%) 48 (96%) 48 (96%)

どれも成績が良いですが、意外にも、摘要をカテゴリ型とした実験1は完璧で、テキスト型にした実験2、実験3は少しだけ成績が悪くなっています。

翌月50件のデータを見直してみると、日付は当然異なりますが、金額はほぼ同じような値だし、摘要の項目は学習データに存在するものと同じものばかりでした。自分の行動パターンがいかに固定化していることが良く分かります(笑)。

ところで、実験1は摘要をカテゴリ型としているため、未知のデータ(例えばコンビニの店名が異なるなど)に対してはどうかな?と思って、コンビニの店名を変えて試してみましたが、正しく予測していました。もっとも、テストデータとは大きくかけ離れた(自分の行動パターンにはない)データを与えると、実験1に限らす、全て低いスコアの予測でした(当たり前ですね)。

この簡単なテストだけみると、実験1が良いように見えましたが、その理由を知りたくなって、AutoML Tables の評価タブに表示される各種指標を見てみることにしました。

(2)モデルの評価指標

作成したモデルの各種評価指標は、AutoML Tables の「評価」タブで確認できます。
以下は、実験1~3のサマリー指標です。(スコアしきい値:0.5)

実験1 実験2 実験3
AUC PR 0.934 0.895 0.927
AUC ROC 0.987 0.977 0.985
適合率 91.74% 91.3% 92.31%
再現率 76.34% 74.34% 75.68%
ログ損失 0.484 0.596 0.477

サマリーを見たところでは、実験1が突出して良いという印象はありませんので、これだけだと実験1の結果が良かった理由が正直よく分かりません。

しかし、AutoML Tables ではサマリーだけでなく、各ターゲットの固有値ごと(今回の例では勘定科目ごと)に、F1スコア、適合率、再現率、偽陽性率、適合率/再現率曲線、受信者操作特性曲線などを見ることができます。

データが多いので割愛しますが、勘定科目ごとに評価値を見ていくと、適合率と再現率が100%近くあるような、明確に判定できている勘定科目は実験1~3で共通していました。しかし判定基準が曖昧そうな(判定が難しい)データ?に対しては、それぞれ得意な勘定科目と不得意な勘定科目がある、という印象でした。

また、適合率や再現率が低い勘定科目についても、「スコアのしきい値」を調整すると、もう少しスコアが改善する勘定科目もありました。

しかし、正直なところ、これらの評価指標だけでは、実験1の結果が未知データに対して思ったより良かった理由は分かりませんでした。そこで、特徴量の重要度も見てみることにします。

(3)特徴量の重要度

AutoML Tables の「評価」タブ画面を下にスクロールしていくと「特徴量の重要度」のグラフが表示されてます。

「特徴量の重要度」とは、画面のヘルプで以下のように説明されています。
  • 各入力特徴の相対的な重要度。スコアは、合計が 1 になるように正規化されています。重要度スコアは、列の値が変化したときに予測がどの程度変化するかによって決まります。予測の変化が大きいほど重要度が高いことを示します。

細かな定義はともかく早速見てみます。

<実験1の特徴量の重要度>
description:73%、amount:15%、date:11%

AutoML Tables/実験1の特徴量の重要度


<実験2の特徴量の重要度>
description:91%、amount:7%、date:2%

AutoML Tables/実験2の特徴量の重要度


<実験3の特徴量の重要度>
description:68%、date:18%、amount:14%

AutoML Tables/実験3の特徴量の重要度


実験1~3の重要度を見比べると、摘要(description)が最も重要度が高いのは共通していますが、重要度の比率がモデルによって違うことが分かります。

特に、実験1と実験2の重要度は「金額 > 日付」ですが、実験3は「日付 > 金額」となっています。実験3においては、日付の方が金額より重要度が高いようです。

(1)で、実験1は摘要をカテゴリ型としているのに、コンビニの店名を変えてもうまく予測できていたのは、どうやら摘要を無視して金額が優先されたからかもしれません。(逆に、実験2と実験3は摘要のテキストを評価しようとしたかも。)

試しに、実験1のモデルは、金額は同じで摘要を空文字列にしても高スコアで正解しました(笑)。一方、実験2と3のモデルは、金額が同じで摘要が空文字の予測はハズレました(スコアも低かったです)。

考えて見ると、私が勘定科目を設定するとき、ほぼ摘要に書かれている内容か金額で決めていて、日付はあまり意識することがありません。

カード利用履歴としての日付はとても重要な情報ですが、今回対象としている、私個人の勘定科目の決定には、日付は重要度がとても低いと思いました。
(さらに進めて私の場合は金額だけでも予測できるのでは?と思いましたが、さすがにそれは同じ金額帯の区別がつかないので、それは無理です…)

ということで、分かりやすく、特徴量から日付を削除して実験することにしました。

(参考)
AutoML Tables が提供する特徴量の重要度には、モデルのトレーニングに最も寄与した特徴(モデル特徴量の重要度)と、個々の予測に最も寄与した特徴(ローカル特徴量の重要度)があります。
上記の内容は「モデル特徴量の重要度」を見たものです。今回は「ローカル特徴量の重要度」を試していませんが、見れば理由が明確になるかもしれません。


(4)モデルタイプとサイズの違い

少し脇道?にそれますが、面白いことに、実験1、実験2、実験3では、モデルのアーキテクチャが変わっていました。

モデルのアーキテクチャが変わると、モデルサイズのオーダーも変わります。

以下は、実験1~3のモデルタイプ(トレーニングログより抽出)とモデルをエクスポートした際のファイルサイズです。

実験1 実験2 実験3
モデルタイプ GBDT AdaNet nn
モデルサイズ 285.5 kB 1.7 MB 11.1 MB

  • GDBT => 勾配ブースト ディシジョン ツリー モデル
  • AdaNet => AdaNet モデル
  • nn => フィードフォワード ニューラル ネットワーク モデル
AutoML Tables がモデルのアーキテクチャを自動決定するので、利用者がアーキテクチャを指定することはできませんが、トレーニングデータによってモデルが変わる可能性があることは分かりました。

(参考)
以下はAdaNet モデルの概要です。

[4]特徴の変更

特徴量から日付を削除して実験してみます。

(1)新しいデータセットの作成

既にトレーニングデータを登録しているデータセットから、ある特徴を除いて再度学習を行いたい場合は、トレーニング開始画面にある「入力特徴量の選択」で学習から除外したい項目のチェックを外してトレーニングを開始します。
但し、この場合は既存のデータセット内に特徴が異なるモデルが混在しますので、ちゃんと管理する必要があると思います。

今回は、日付(date)項目を削除した新しいトレーニングデータを作成し、異なる名前で新しいデータセットを作成してトレーニングすることにしました。

まず、元のトレーニングデータ(CSVファイル)からdate(日付)カラムを削除しただけのCSVファイルを作ってデータセットにインポートしました。しかし、トレーニングを開始すると以下のエラーが発生して停止しました。
Missing label(s) in test split: target column contains 11 distinct values, but only 10 present. There must be at least one instance of each label value in every split.

日付を削除したことで、ある勘定科目に問題が発生したようです。
調べてみると、同じ摘要の文字列、金額が定期的に記載されていた勘定科目が問題になったようで、日付を削除すると同じデータの連続となっていました。ちなみに、この勘定科目は、実験1~3において精度、適合率、再現率ともに100%で完璧な予測ができていました。

本当は日付を削除しただけの条件で実験1~3と比較してみたかったのですが、今回は手っ取り早く当初のエラー対応と同様に「不明」科目に割り当てて、まずは実験を通すことにしました。

<準備した日付削除版トレーニングデータ>
  • 特徴
    • description(摘要)
    • amount(金額)
  • ターゲット
    • account(勘定科目)
      • 1クラス削除して、10クラスとしました。

description,amount,account
病院名,1210,医療費
(以下同様)

(2)モデルの評価指標

日付削除版トレーニングデータを使って2つモデルを作りました。
  • 実験4
    • 実験1と同様に、摘要をカテゴリ型でトレーニングしたもの
  • 実験5
    • 実験3と同様に、摘要をUnicode正規化し、かつスラッシュを半角空白にしてテキスト型でトレーニングしたもの

以下はモデルのサマリー指標と、モデルタイプ、サイズです。

実験4 実験5
AUC PR 0.933 0.962
AUC ROC 0.982 0.99
適合率 92.86% 94.53%
再現率 77.78% 81.76%
ログ損失 0.510 0.335
モデルタイプ AdaNet AdaNet
モデルサイズ 731.0 kB 1.8 MB

結果としては、実験5(Unicode正規化し、スラッシュを空白にしたテキスト型)は今までで最も良いモデルになっているように見えます。

実験4においても、AUC PRだけみると、前に成績が良かった実験1と同程度ですが、それ以外の適合率や再現率などが向上しています。

トレーニングデータが少し変わっているので、直接比較はできませんが、日付を削除したら性能が向上したように思えます。

なお、評価指標以外で興味深いのは、今回のモデルタイプが、実験4、実験5ともに AdaNet になっていることです。サイズの違いは、トレーニングデータのカテゴリ型とテキスト型の違いによるものと思います。

(3)特徴量の重要度

特徴量の重要度は、日付が入っていた時と違って、実験4と実験5ともに重要度の比率が同程度になっています(amount(金額)は7%)。

<実験4の特徴量の重要度>
description:93%、amount:7%

AutoML Tables/実験4の特徴量の重要度

<実験5の特徴量の重要度>
description:92%、amount:8%

AutoML Tables/実験5の特徴量の重要度


(4)混同行列

ターゲットの固有の値を11個から10個に減らした副産物?として、「評価」タブ画面の下側に混同行列が表示されるようになりました。
(AutoML Tables の仕様として、「混同行列は、ターゲット列の値が10以下の分類モデルにのみ摘要される」とあります。)

これは何が得意で何が不得意なのか、直観的に分かりやすいです。

判定が混乱しているところの原因になっているトレーニングデータの問題に関しては後述します。

<実験5の混同行列>

AutoML Tables/実験5の混同行列

(5)翌月50件のデータで試してみる

最後に、翌月50件のデータで比較してみました。結果は以下の通りです。

実験4 実験5
翌月50件の正解数 49 (98%) 49 (98%)

実験4と実験5ともに1問だけ不正解でした。

この不正解だったデータを確認すると、今回、日付を削除したデータをトレーニングできるように、手っ取り早く「不明」科目に割り当てたものでした。

もとの勘定科目は、摘要や金額がほぼ一定で、実験1から実験3まで正解していていましたが(適合率、再現率ともに100%でした)、「不明」科目に割り当てて雑多なデータと混ざってしまったために識別が難しくなってしまったように思えます。

ちなみに、実験4の「不明」科目の評価指標を見ると、再現率が0%(全て偽陰性)でした。一方、実験5では「不明」科目の適合率と再現率は、それぞれ95%、96%でしたので、実験4と実験5では間違えた事情が異なるようです。

今回は深く考えずに「不明」に割り当ててしまったために不正解となりましたが、もともと識別できる科目なので、気持ち的には全問正解レベルです(笑)。

加えて、先の実験と同様に、コンビニの店名を変えたりして、少し未知データを試してみたところ、実験5がとても良くなりました。(摘要を空白にした同様のテストも正解するようになりました。)
逆に、カテゴリ型の実験4は、未知データに関して実験1より成績が悪い印象でした。

ということで、とてもアドホックな対応ながら、自分用に限れば改善しました。

他にもありますが、無料トライアルを使い果たしてしまったし、長くなりますし、元データを公開できないので(笑)、本記事ではこれ以上追究しないことにします。

[5]使ってみた印象

ダラダラとかいてきましたが、ここまで試した印象をまとめておきます。

(1)AutoML Tables の印象

まず、これは凄いです!

トレーニング用のCSVファイルがあれば、手軽に機械学習を行うことができます。また、作成したモデルで予測するための方法もいろいろと用意されています。このためプログラミングやサーバ運用の知識があまり無くても、学習から運用までの一連の流れを実現できます。

学習済みデータはエクスポートして再利用も可能です。

また、良くも悪くも設定項目が殆ど無いし、画面操作もシンプルで使いやすいです。

そして、何より作成されるモデルも素晴らしいです。その実力を実感します。

機械学習に関する知識があまり無くても簡単に始められるという点で、初めて機械学習に触れる人にもお勧めできるものですが、その実力は初心者向けレベルではなく、凄いです。

一方で、モデルを評価して改善しようとすると、やはり関連知識は必要だと思いました。

AutoML Tables は機械学習の技術的な要素をかなり自動化してくれますが、良いモデルを作る前提となる、トレーニングデータの質はユーザ次第ですし、モデルの評価指標から自動的にトレーニングデータの改善が行われるわけでもありません。

その意味で、対象となるデータに関する知識(業務知識)やモデルの評価指標を見る知識は必要だと感じました。
ただ、これは一般的な機械学習で言われることと同じですので、技術面の負担を大幅に軽減してくれることを考えると、本来考えるべきことに集中できるとも言えます。

ということで、AutoML Tables のサービス内容には感動していたのですが、(会社員としてではなく、)個人で AutoML Tables を利用するにはコスト面で少し不安も感じました。

セコイ話ではありますが、実は実験1~実験5までで、ほぼ無料トライアルを使い果たしました(涙)。思った以上に無料枠が少なかったです(というより、トレーニングにはコストがかかるということかな)。もっとやりたかった(笑)。

どちらにしても、AutoML Tables は手軽に使えるので、どんどん試してモデルを改善したくなりますが、「ご利用は計画的に」という感じです。

(2)自分用の勘定科目判定に関する印象

結論として、予想以上に良いモデルが出来ました!

今回の実験レベルのモデルができれば、自分でわざわざルールを考えてプログラミングする必要は無くて、十分置き換え可能だと感じました。

とはいえ、実際に利用することを考えると、不安要素もあります。
ルール化したものは答えに至る理由が明確なので安心感がありますが、機械学習に置き換えると、結果が合っているかどうか不安があります。もし結果を全て確認する必要があるなら面倒が増える気がして使う気になれません。

これに関しては、今回の実験を通して、間違っているものについては、共通してスコアがかなり低い値になっていましたので、例えば、しきい値以下の値の場合は「判定不能」として答えを出さず、自分で設定しなさい、とすれば良いと思いました。
判定不能なもののみ確認して設定する方が安心だし、今回の実験を通して「判定不能」となるものは、実際にはかなり少ない気がしました。

実を言うと、AutoML Tables で学習を始める前にトレーニングデータの内容をほとんど確認していなかったのですが、後で内容を再確認すると、技術以前のいろいろな問題があることに気づきましたので(気づくのが遅かった…)、いくつかメモしておきます。
  • 同じ摘要の文言でも、勘定科目が異なるものがあり、それが成績を下げた。
    • 分かりやすい例は、Amazon で購入したものです。摘要には「AMAZON.CO.JP」と書かれているだけですが、書籍やCD、日用品など、雑多に含まれています。一度にまとめて買ったものについては、過去データでは適当に勘定を割り振っているものが多く、自分でも混乱します。
  • 勘定科目の割り振りの曖昧さ
    • 今回の実験で気付いたのですが(涙)、例えば「日用品」と「消耗品」という勘定科目がありました。この区別はかなり適当に行っていて、同じものでも異なる勘定に割り振っているものも多くありました。
  • 利用傾向が変わる
    • 昔から変わらないパターンが多いのですが、3年前には頻出のパターンが、最近では全く別のパターンに置き換わっていることもあります。トレーニングデータの作り方の難しさを感じます。

結局は機械学習を利用したパターン認識なので、パターンを認識しやすいようなトレーニングデータを作る必要があるのですが、そのために自分の行動を変えるのも嫌だし、ヒントを増やすために他の情報を組み合わせて…、というのも大変だし(笑)。

等々、考えると課題が多いように思えますが、今回の自分用の勘定科目判定には十分な精度が出ているので、今はとても満足しています。

[6]最後に

AutoML Tables は、始めるための敷居がとても低くて、またモデルのトレーニングやテストは、ほとんどゲーム感覚で、とても面白かったです。

どちらが良いというわけではありませんが、業務アプリの設計や実装とは少々感覚が異なります。ただ、どちらにしても上流工程や評価が大事だということは同じだと実感しました。

続いて、(いつか)銀行の取引履歴も自動化しよう…


コメント

このブログの人気の投稿

VirtualBoxのスナップショット機能

Google Document AIで画像から表形式データを抽出する(Vision API OCRとの違い)

Ubuntu/Colab環境でPDFファイルのページを画像化する(pdf2image、pdftoppm、pdftocairo)