テスト項目を自動作成できるか

こんにちは。
第二ソリューションの K.S です。

はじめに

xUnit が公開されてから最近のテスト工程では自動化が徐々に浸透しはじめている。
自動化といっても、テスト用のプログラムを書く必要があるだけでなくテスト対象となっているプログラムを変更するとテストプログラムの変更も必要になり、まだまだ開発が楽になったというわけではない。
このことは自動化ツールを WebDriver に変えても RPA に変えても同じである。
とはいえ、ライブラリの変更や設定変更などのプログラムへの影響を知る為にはテストが自動化されていなければ大変な工数になるので、それなりのメリットを享受することになる。
さて、最近は仕様書からテスト項目を自動生成できないかと思うようになったのは、ChatGPTが昨今様々なメディアで取り上げられているからである。
しかし、自然言語処理なんていう分野は、聞いたことはあっても全く知らない分野である。
調べてみるとチャットシステムの検索処理や顧客サポートなどのシステムなどには組み込まれているらしいことを最近になって知るようになった。
全く知らないというわけにはいかない、という思いを益々持つようになり試してみることにした。

BERT

今、試してみたいことはテスト項目の自動生成である。
但し、情報漏洩の観点からローカル環境でできるものが望ましい。
そこで、ローカル環境で簡単に試せるようなものはないかと探していると BERT というのを見つけた。
この BERT を知るためには、Transformer、Attention を知る必要があることがわかった。
Transformer は、異なる機能を持つ Attention を構成して成り立っているものらしい。
このことによって、機械翻訳、文書分類、情報抽出などのタスクを BERT だけで行えるようになったということだ。
かなりすごいことのなのかもしれないが、自然言語処理の開発経験がない私にとってはあまりピンとこない。
とにかく先へ進むことにしよう。

Attention

形態素解析は主に品詞分解を行い、機械学習を使った自然言語処理では、単語や文をベクトルに変換して演算しているようである。
かなり有名な例のようだが、「王様」-「男」+「女」=「王妃」のような演算ができるということだ。
このベクトルは数百次元にも及ぶらしい。性別、感情など様々な要素が数値化されているものと思われる。

Attention には、主に Self-Attention、Scaled Dot-Product Attention、Multi-Head Attention がある。
文を解析する際に、「今年の暑さは夏のせいだけではなく温暖化の影響もあるからでしょう。」という文があった時に、”今年”という単語は、”ある”とも関係している。
もし、”今年”が”昨年”に変わると”ある”が”あった”に変わる。また、”暑さ”は”夏”と”温暖化”とも関係している。
このような関係性を Self-Attention では考慮できるようになった。
このことによって、単語ベクトルを逐次的に非線形変換を繰り返し文ベクトルを生成するより表現力の高い文ベクトルへ変換ができるようになった。
Scaled Dot-Product Attention は、計算を効率よく行うための機能を提供していて、数百次元のベクトルの演算を行うのは高い計算能力と多くのメモリが必要になるが、類似度を使うことで計算効率とメモリ使用量の削減を実現させている。
この類似度とは、二つのベクトルをある関数に入力して数値を出力するもので、この数値が大きいほど類似度が高いということになる。
二つの文ベクトルを入力して大きな数値が出力されれば、似たような文と判断される。
Multi-Head Attention は、単語ベクトルから複数のベクトルを生成することで、一つの単語を複数の観点、多義性を考慮するための機能のようだ。
一旦ここまでの理解の真偽はさておき、先へ進むことにしよう。

BERT の質問応答タスク

BERT の質問応答タスクは、「今日の天気は?」のような質問に回答するようにはできていない。
文章と質問を入力して、文章の中から回答を抽出する狭義の質問応答タスクになる。

そこで、テスト項目を挙げてもらうために以下のプログラムを作成して試してみた。
context の中から question の回答が抽出されるように文章を作成する必要がある。

その前に、どんなものなのか感じをつかむために練習してみよう。

from transformers import AutoModelForQuestionAnswering, AutoTokenizer, pipeline

model_name = "distilbert-base-uncased-distilled-squad"

nlp = pipeline('question-answering', model=model_name, tokenizer=model_name)

inputQA = {
'question': "会社はどこにあるの?",
'context': '新潟県にある東京アプリケーションシステム株式会社はシステムを開発する会社です。'
}

res = nlp(inputQA)
print('答え: ' + res['answer'])

答え: 新潟県にある

「東京」と回答されてもおかしくはないように思われるが、期待する回答を文章の中から抽出できているようだ。

次に、以下のような文章で試した。

inputQA = {
'question': "会社はどこにあるの?",
'context': '東京アプリケーションシステム株式会社は新潟県にあるシステムを開発する会社です。'
}

答え: 会社

“どこに” を “新潟県” に関連付けるのが難しいのだろうか。
次に、抽出し易そうな文章にしてみた。

inputQA = {
'question': "会社はどこにあるの?",
'context': '東京アプリケーションシステム株式会社はシステムを開発する会社です。会社は新潟県にあります。'
}

答え: 会社は新潟県にあります。

“会社” は “会社”、”どこに” は “新潟県”、”ある” は “あり” に関連付けられたのではないか、と思われる。

inputQA = {
'question': "会社は東京にあるの?",
'context': '東京アプリケーションシステム株式会社はシステムを開発する会社です。会社は新潟県にあります。'
}

答え: 会社は新潟県にあります。

間違わなかった。

文章の作り方で、ちゃんとした回答は得られそうな感じである。

次に、質問からテスト項目を挙げるような文章を作成できるかどうかを試してみよう。
商品一覧画面からりんご5個以上の購入者に対しては注文画面に遷移し、りんご5個未満は警告メッセージを出力する、という仕様を想定している。

inputQA = {
'question': "正常の場合について挙げて下さい。",
'context': '正常の場合は、注文ボタン押下時はりんご5個以上で注文画面に遷移する。異常の場合は、りんご5個未満は警告メッセージを出力します。'
}

答え: 正

正常の場合は、~遷移する。までを抽出してくれればよいのだが、がっかりする回答である。

質問応答を以下のように変えてみた。

inputQA = {
'question': "正常の場合について挙げて下さい。",
'context': '注文ボタン押下時はりんご5個以上で注文画面に遷移する。りんご5個未満は警告メッセージを出力します。'
}

答え: メッセージを出力します

思ったような回答は得られない。

再び、質問応答を以下のように変えてみた。

inputQA = {
'question': "注文ボタン押下時の処理を挙げて下さい。",
'context': '注文ボタン押下時はりんご5個以上で注文画面に遷移する。りんご5個未満は警告メッセージを出力します。'
}

答え: 注文

かなり丁寧に質問を作成したつもりだが、難しいようである。
抽出できそうな質問をしてみるが。

inputQA = {
'question': "りんご5個以上の場合について挙げて下さい。",
'context': '注文ボタン押下時はりんごの個数をチェックし5個以上で注文画面に遷移します。りんご5個未満は警告メッセージを出力します。'
}

答え: メッセージを出力します。

文章を「生成」するような質問には回答が得られないようである

まとめ

質問と文章の作り方次第では回答を得られることがわかった。

BERT には、事前学習とファインチューニングというのがあるらしい。
事前学習には、一般的なデータが使用されているであろう。テスト項目生成というのは、かなり特殊な用途だろうと思うとファインチューニングを行えば期待する回答が得られるかもしれない。
今後は、ファインチューニングあたりを調べてみたいと思う。