見出し画像

AIの作ったプログラムを添削してみた

一昨日、ログ分析ツールで使うTF-IDFのプログラムをAI(Google Bard)に相談しました。
TFIDFをGO言語で作りたい」
という質問でした。答えは

はい、日本語で答えます。

TFIDFとは、自然言語処理などで、文書中に含まれる単語の重要度を評価する手法の一つです。TFIDFは、TF(Term Frequency、単語の出現頻度)とIDF(Inverse Document Frequency、逆文書頻度)の二つの指標から計算されます。

TFは、ある単語が文書内に出現した回数を、文書中の単語数で割ることで計算されます。IDFは、ある単語が出現した文書数を、文書全体の文書数で割り、さらにその結果に自然対数をとることで計算されます。

TFIDFは、文書の類似度を計算したり、文書の重要度を評価したりするために使用されます。

Go言語でTFIDFを計算するには、次の手順に従います。文書を単語に分割します。
各単語の出現頻度を計算します。
各単語の逆文書頻度を計算します。
各単語のTFIDF値を計算します。


次のコードは、Go言語でTFIDFを計算する例です。

Google Bard

でした。
そこで、作ってもらったプログラムは、

package main

import (
  "fmt"
  "strings"
)

func main() {
  // 文書を取得します。
  document := "今日はいい天気です。"

  // 文書を単語に分割します。
  words := strings.Fields(document)

  // 各単語の出現頻度を計算します。
  termFrequencies := make(map[string]int)
  for _, word := range words {
    termFrequencies[word]++
  }

  // 各単語の逆文書頻度を計算します。
  inverseDocumentFrequencies := make(map[string]float64)
  for word, termFrequency := range termFrequencies {
    inverseDocumentFrequencies[word] = float64(len(words)) / float64(termFrequency)
  }

  // 各単語のTFIDF値を計算します。
  tfidfValues := make(map[string]float64)
  for word, termFrequency := range termFrequencies {
    tfidfValues[word] = termFrequency * inverseDocumentFrequencies[word]
  }

  // TFIDF値を表示します。
  for word, tfidfValue := range tfidfValues {
    fmt.Println(word, tfidfValue)
  }
}

実行結果は

今日は 0.577
いい 0.577
天気です 0.577

Google Bard

なると教えてくれました。

なんとなく、正しいそうに見えます。一瞬、AIはすごいなと思いましたが、
よく見ると、いろいろ間違ってそうです。

そもそも、このままでは、エラーで実行できません。

AIなのに間違える

AIなのにGO言語の文法を間違えています。
上の緑の矢印のところは、ちゃんと数値の型を変換していますが、
赤い矢印のところは、なぜか間違えています。
ここを修正して実行できるようになりました。
表示された結果は

今日はいい天気です 1

です。AIが言っている答えと全然違います。
そもそも

// 文書を取得します。
 document := "今日はいい天気です。"

// 文書を単語に分割します。
 words := strings.Fields(document)

これでは、日本語の文章を単語に分割できません。
AI の回答に合うような単語の分割ができるように

// 文書を取得します。
  document := "今日は いい 天気です"

  // 文書を単語に分割します。
  words := strings.Fields(document)

のように修正してみましが、答えは合いません。
このあたりAIの言っていることが怪しいのではないかと思い、
TF-IDFについて、まじめに調べることにしました。
検索して沢山のサイトを見ましたが、解説が間違っていたり、曖昧
なものがありました。
調べた結果、計算式は

TF-IDF=単語の出現頻度(TF)✕単語のレア度(IDF)
TF=文書中のある単語の出現数/文書中の全単語の数
IDF=Log(全文書数/ある単語の出現する文書数)+1

でしたが、AIが作ったプログラムの計算は完全に違ってました。
例えば

TF=文書中のある単語の出現数

となっています。AIの日本語の解説には、IDFの計算に自然対数がでてきあmすが、プログラムの方にはLogの計算がでてきません。
AIの作ったプログラムを添削して、答えを合わせようと思って始めたのですが諦めました。修正してすむようなプログラムではなかったのでした。
日本語の扱えるTF-IDFのパッケージ

を見つけたので、これ使ってAIが作ったプログラムの
「今日は天気がいいです。」
だけ活かして、TF-IDFを計算するプログラムを作ってみました。

package main

import (
	"fmt"

	"github.com/ramenjuniti/jtfidf"
)

func main() {
	ds := []string{
		"今日はいい天気です。",
	}
	fmt.Println("TF:", jtfidf.AllTf(ds[0]))
	fmt.Println("IDF:", jtfidf.AllIdf(ds))
	fmt.Println("TFIDF:", jtfidf.AllTfidf(ds))
}

です。実行すると

TF: map[。:0.16666666666666666 いい:0.16666666666666666 です:0.16666666666666666 は:0.16666666666666666 今日:0.16666666666666666 天気:0.16666666666666666]
IDF: map[。:0 いい:0 です:0 は:0 今日:0 天気:0]
TFIDF: [map[。:0.16666666666666666 いい:0.16666666666666666 です:0.16666666666666666 は:0.16666666666666666 今日:0.16666666666666666 天気:0.16666666666666666]]

のようになります。TTIDFの値はあってますが、IDFの値は間違っています。
パッケージのソースコードを読んでみましたが+1するのを忘れています。
TFIDFの計算の時に+1しているのでTFIDFでは辻褄が合っています。

AIに聞くと尤もらしいプログラムをコメント付きでキレイに整形して、自信たっぷりに教えてくれるので信じてしまいそうです。
でも、人間の書いた沢山の間違った文章から学習しているので、間違ったプログラムを教えてくれているのかもしれません。
助手の猫が天から
「それは、人間の作ったプログラムも同じじゃ!」
と言っています。

明日に続く

この記事が参加している募集

開発のための諸経費(機材、Appleの開発者、サーバー運用)に利用します。 ソフトウェアのマニュアルをnoteの記事で提供しています。 サポートによりnoteの運営にも貢献できるのでよろしくお願います。