はじめに
こんにちは、一休.comデータサイエンス部の平田です。 みなさんChatGPT活用してますか?
一エンジニアとして便利に使ってはいるものの、自社サービスにどのように組み込もうか模索しているところも多いかもしれません。 一番の利用先として思いつくのが、自社の情報をもとに質問に答えるチャットボットではないでしょうか。
その中では、ハイコンテキストな検索(例えば、「東京から2時間以内で子供も楽しめるアクティビティがあって、景色も良い宿」のような)にも答えられるとボットの価値が増します。 ChatGPTが事前に学習した内容では古く、正確ではないためそういった検索に応えるのはかなり厳しいです。
そのため、こちら側が持っているデータを渡してあげる必要があるのですが、今回はその自社の情報をどう組み込むのか、という部分についてご紹介します。
素のChatGPTでは?
ChatGPTに例えば「熱海で子供も楽しめるアクティビティがあって、景色も良い宿を教えてください」 を聞いてそのまま納得できる内容が返ってくるなら、組み込むだけで済むのでかなり楽です。 まずは試してみます。
熱海は温泉地として有名で、子供から大人まで楽しめる観光地がたくさんあります。以下は、子供も楽しめるアクティビティと景色の良い宿泊施設の一例です。 アクティビティ: 熱海海洋公園: 水族館で、色とりどりの魚やイルカショーなどが楽しめます。 熱海梅園: 季節によっては梅の花が楽しめる庭園で、散策やピクニックが楽しめます。 熱海サンビーチ: 夏場は海水浴が楽しめるビーチで、子供たちに人気です。 MOA美術館: アートに興味を持つ家族には、美術館の散策もおすすめです。 宿泊施設: アタミセイザンリゾート: 子供向けのプールや遊び場があり、家族連れに人気。部屋からの海の眺めも素晴らしいです。 ホテルニュー熱海: 温泉が楽しめる宿で、子供用のアメニティも充実しています。海を一望できる部屋もあります。 熱海温泉 ホテルマイクローネ: 子供向けのプレイルームやプールがあり、温泉も楽しめます。部屋からの景色も良いです。 これらの施設は、子供向けのアクティビティと素晴らしい景色を楽しめるため、家族旅行にぴったりです。予約時には、子供向けの設備やサービスがあることを確認すると良いでしょう。最新の情報や詳細は、公式ウェブサイトや旅行サイトで確認することをおすすめします。
アクティビティと宿を出してくれるのでぱっと見は良さそうですが、熱海海洋公園はありませんし、宿泊施設に至っては3つともありません。 ChatGPTの学習データは2021年9月までのものなので、かつてはあったのかもしれません。 やはり、最新性のある情報を提供させるために、こちらから与えてあげる必要がありそうです。
情報を組み込む方法
ファインチューニング
質問文と答えの対を大量に用意し、それを事前学習されたモデルに対してさらに学習させることでオリジナルのモデルを作る方法があります。
質問文と答えの対を人手で作るのも難しいので、ChatGPTにやらせます。
response = openai.Completion.create(
engine=COMPLETIONS_MODEL,
prompt=f"""次の複数の口コミから、50文字以内の日本語の質問文を1つ生成してください。
\n\nテキスト: {context}\n\n質問文:\n1.""",
temperature=0.8,
max_tokens=400,
top_p=1,
stop=["\n\n"]
)
prompt=f"次のテキストに基づいて質問に答えてください\n\nテキスト: {row.context}\n\n質問文:\n{row.questions}\n\n答え:\n1.",
ただ、質の高い対を大量に作るのは難しく、学習としてもあまりいい結果になりませんでした。(例はcurieモデル)


(↓見ると難しそうですが…)
ChatGPT の Fine-tuning を試したけど上手くいかなかった話
埋め込みベクトル表現
ファインチューニング以外にも情報を渡す方法としては、プロンプトに必要な情報がまとまった文章を加えておき、 それに基づいて文章を生成してもらうというものがあります。
そのためには、「どの情報を渡すか」という部分をこちらで選択する必要があります。 ChatGPTのtoken数の制限、価格を考慮すると全てを渡すことはできません。 必要最低限の量を渡す方法として、クエリをベクトル化して、あらかじめベクトル化した情報と類似度が高いものだけをプロンプトに加えるようにします。
ファインチューニングと比較したとき、渡す情報をコントロールしやすいメリットがある反面、プロンプトが肥大化しやすいというデメリットがあります。
検証
今回は、口コミの中から必要な情報だけを抽出できるのか?というところをトライしてみます。 ベクトルで類似度をスコアリングしたのち、各項目について言及しているかどうかを正規表現で正誤判定させることにしています。
検証ではOpenAIのembedding APIを使用しています。対象は単語になっていますが、任意の文章をベクトル化することができます。
また、英語だと精度が良いらしいですが、ベクトルマッチングにおいてもそうなのかついでに調べてみます。 ちなみに翻訳にはdeepL APIを使いました。
querys = {'朝食': ['朝食', '朝ごはん', '朝ご飯', '朝御飯', '朝餉', 'ブレックファースト'],
'ペット': ['ペット', '犬', 'わんこ', 'ドッグ', 'わんちゃん','愛犬','ワンコ','ワンちゃん'],
'花火': ['花火'],
'絶景': ['絶景', '景色がいい', '景色が良い', '景色のいい', '景色の良い'],
'バリアフリー': ['バリアフリー', '車椅子', '車いす', '足が悪い', '脚が悪い'],
'有名建築家': ['有名建築家','隈研吾','安藤忠雄','北川原温','坂茂','山口隆','岸本和彦']}
target_query = '有名建築家'
target_vec = get_embedding([target_query])[0]["embedding"]
target_query_eng = translate_text([target_query])[0]
target_eng_vec = get_embedding([target_query_eng])[0]["embedding"]
df['test'] = df.review_text.apply(lambda x: any([q in x for q in querys[target_query]]))
df['jpn_score'] = df.embedding.apply(lambda x: calc_cossim(target_vec, x))
df['eng_score'] = df.embedding_english.apply(lambda x: calc_cossim(target_eng_vec, x))
ROC曲線は、スコアの閾値を0~1で動かしたときに、横軸に疑陽性の率、縦軸に真陽性の率をプロットしたものです。 下側の面積をAUCと呼び、1なら完全な分類、0.5ならランダムな分類と同程度の精度だと評価されます。


| 項目 | 日本語 | 英語翻訳 |
|---|---|---|
| 朝食 | 0.83 | 0.88 |
| ペット | 0.92 | 0.85 |
| 花火 | 0.95 | 0.97 |
| 景色 | 0.90 | 0.83 |
| バリアフリー | 0.63 | 0.78 |
| 有名建築家 | 0.95 | 0.95 |
単語によって、日本語が良かったり英語が良かったりバラバラですね。 明らかに英語の精度が良くなるかと思ったので意外でした。 有名建築家は正解データ数が少ないので両者高くなっています。
実際の口コミ抽出例
施設、キーワードを入力するとマッチした口コミを返すAPIを作りました。 口コミの一部分を返すことで、プロンプトが長くなるのを防ぐ工夫もしています。
以下はkeyword=温泉をとある施設の口コミを対象に入れたときの一例です。
一つ目は「温泉」がちゃんと入っていますね。二つ目は温泉に近い「温水プール」が入っています。
{
"hotel_id": "00002627",
"review_id": "1000022712",
"review_text": "3連泊させていただきました。主にホテルにこもって過ごすことを前提にお伺いしましたが、客室はとても居心地がよく、朝焼けがとても綺麗に見えました。
また、プールもこじんまりとしていますが十分楽しめました。
特に23時までオープンしていることからナイトプールはとても綺麗なライティングで、夜空も綺麗に眺めることができました。
また、料理のレベルが高く、味も見た目にも楽しめるものばかりでした。
盛り付けはとってもオシャレでした(部屋食もよかった)。
接客は過度なものはなく、他のホテルに比べるとややあっさりした印象ですが、感じの良い方達ばかりでした。
温泉・お風呂は星4つにしたのですが、特段不満に感じることはありませんでした(水圧、綺麗さなどどれも満足)。
館内至るところ、アロマの香りが楽しめたのもよかったです。またお伺いしたいです。ありがとうございました。",
"score": 0.75787675,
"matches": [
{
"match_text": "温泉・お風呂は星4つにしたのですが、特段不満に感じることはありませんでした(水圧、綺麗さなどどれも満足)。館内至るところ、アロマの香りが楽しめたのもよかったです。",
"positive_score": 0.89829713,
"score": 0.75787675
},
{
"match_text": "また、プールもこじんまりとしていますが十分楽しめました。",
"positive_score": 0.89906454,
"score": 0.71830595
}
]
},
{
"hotel_id": "00002627",
"review_id": "1001226432",
"review_text": "10月30日宿泊\n客室はゆったりしていて寛げる\n温水プールは温かく景色が良い\n
レストランはおしゃれな空間で\n雰囲気が良い\nスタッフは一生懸命で好感が持てる\n来月またお世話になります",
"score": 0.7502389,
"matches": [
{
"match_text": "10月30日宿泊\n客室はゆったりしていて寛げる\n温水プールは温かく景色が良い",
"positive_score": 0.779632,
"score": 0.7502389
},
{
"match_text": "レストランはおしゃれな空間で\n雰囲気が良い",
"positive_score": 0.79234755,
"score": 0.7080128
}
]
}
まとめ
今回は自社の情報をChatGPTに組み込む方法をご紹介しました。
しかし実はまだ、冒頭の「熱海で子供も楽しめるアクティビティがあって、景色も良い宿を教えてください」に応えられるものが出来ていません。 これは、全国の施設情報をベクトルマッチングで一部に絞っていてもなお量が多くてプロンプトに埋め込むことができないからです。
解決方法についてはChatGPTの次の記事でお伝えできればと思います。
また一休では、ともに良いサービスをつくっていく仲間を積極募集中です!応募前にカジュアルに面談をすることも可能ですので、お気軽にご連絡ください!