ソフトウェアの品質を学びまくる

ソフトウェアの品質、ソフトウェアテストなどについて学んだことを記録するブログです。

テスト管理ツール「PractiTest」を触りながら勝手にテスト管理を語る - その2

 「その1」の続きで、PractiTestによるテストケース作成の話を書いていきます。この感じだと、テストケース作成・管理とテスト実行の話だけで力尽きる予定です。PractiTestの真価はそこじゃないとは思うんだけれど・・・。

www.kzsuzuki.com

探索的テストのサポート

 PractiTestの1つの特徴として、探索的テストをサポートしている点があります。
 サポートといっても特別な機能があるわけではなさそうなのですが、手順付きテストと違う構成になっています。
 入力するのは、「テストチャーター」「ガイドポイント」の2つ。「探索的テスト研究会」のFAQでは、チャーターは以下のように説明されています。

チャーター(またはテストチャーター)とは探索的テストを実施する上での指針となる情報を記したものです。チャーターに記述される項目や情報の粒度は様々で、なにがしかの決まりがあるわけではありません。単に探索する範囲として機能を示した一言程度の非常にシンプルなものもあります。一方、テーマ、目的、優先度、データなど多様な項目をもつかなりボリュームのあるものもあります。

 下の入力例はチャーターとして適切かどうかわかりませんが、「こういうことやっておかしな挙動にならないか確認して」みたいなざっくりとした方針を示すものです。

f:id:kz_suzuki:20191122172656p:plain

 PractiTestではテスト実行時間が計測されていますし、また関連づくイシューにもトレーサビリティがあるので、「時間当たりのイシュー数」といった効率の測定も簡単にできそうです。

テストケースの「呼び出し」

 手順ありのテストケースの方に戻りましょう。ステップ追加のリンクの横に、「他のテストケースを呼び出す」という機能があります。

f:id:kz_suzuki:20191122172833p:plain
他のテストケースを呼び出せる

 テストケース#2のステップとして、テストケース#1を呼んでみると、こう。

f:id:kz_suzuki:20191122173343p:plain
呼び出した結果

 何が起きたかというと、テストケース#2のステップ#1に、テストケース#1がまるごと埋め込まれたということです。
 テストケース#1に書いたことを書き直さなくても、テストケース#2で流用できるのですね。

 これはとても便利そうですが、なかなか使いこなしが難しそうな機能です。テストケース間で呼び出し合うと、テストケース同士の依存性が強くなりすぎて、個々のテストケースの修正がとても難しくなりそうです。
 実際に実行するテストケース自体を直接呼び出すのではなく、「よくある手順」をテストケースの形で部品としてまとめておき、それをテストケースから呼び出すという使い方になるのではないでしょうか。これも明確な運用指針が必要な機能だと感じました。

 さて、こういう呼び出し機能を見たら、やりたくなるのはもちろん、「自分自身を再帰的に呼び出す」ですよね?
 テストケース#2から、テストケース#2を呼び出せるでしょうか? 呼び出せたとすると、テストステップ数は無限大になります。ワクワク。

f:id:kz_suzuki:20191122173009p:plain
ちっ・・・

 Test to call cannont call itself。だよねえ。

テストケースの「データ駆動化」

 もう一つ、細かい話。
 PractiTestは、テストケースのデータ駆動化をサポートしていないようです。*1

 データ駆動化のイメージを説明しておきましょう。
 まずテストケースは、以下のように一部の値をパラメタにしておきます。

ログイン画面で、ユーザ名に「<username>」、パスワードに「<password>」を入力して、「<button>」をクリックする。

 で、これとは別に「データテーブル」として、以下のようにパラメタと値のセットを管理します。

usernamepasswordbutton
空条承太郎oraoraoraログイン
ナランチャ・ギルガboraboraboraリセット

 操作手順と具体的な値を分離しているということです。「手順は同じなんだけれど、いろんな値を試したい」という用途で重宝する機能ですよね。自動テストの文脈ではData Driven Testingと呼ばれるものですが、手動テストでも成立する概念です。

 Squash TMでは、データ駆動化しておいたテストケースを実行に割り当てると、データテーブルの値が操作手順のパラメタに埋め込まれて、複数の具体的なインスタンスとして以下の二つのテストケースに展開されるという仕組みになっています。

  • ログイン画面で、ユーザ名に「空条承太郎」、パスワードに「oraoraora」を入力して、「ログイン」をクリックする。
  • ログイン画面で、ユーザ名に「ナランチャ・ギルガ」、パスワードに「boraborabora」を入力して、「リセット」をクリックする。

 なおPractiTestにも、似て非なる機能があります。データ駆動ではなく、単純なパラメタライズです。
 テストケース時点では以下のように、値を未確定にしておいて、

f:id:kz_suzuki:20191122174932p:plain
2つの中カッコで囲ってパラメタとする。

インスタンスとしてテストセットに割り当てた時点(あるいは実行直前)で、具体的な値を決定するという機能です。二つ以上のパラメタセットを持てるわけではなさそうです。

f:id:kz_suzuki:20191122175123p:plain
具体化しないと、実行時に怒られる

 もちろんこの機能も、直前までパラメタを決められないケースを考慮した、心憎い機能だと思います。
 データ駆動化も仕組み自体は似ているから、拡張してくれないかな・・・。

 テストケース作成はここまで。「その3」に続きます。

www.kzsuzuki.com

*1:もしかすると、Squash TMくらいでしかサポートしていない特殊な機能なのかもしれませんが・・・。

テスト管理ツール「PractiTest」を触りながら勝手にテスト管理を語る - その1

OnlineTestConf Japanの開催が発表されました。開催日は2019年12月7日です。

 主催元のPractiTest社は、テスト管理ソリューション「PractiTest」を提供するイスラエルの企業です。チーフソリューションアーキテクトのJoel Montveliskyさんは今年何度か来日し、国内のいくつかのイベントでセッションをもたれています。たとえばコレ。 connpass.com

 SQiPシンポジウム2019でもブース出展されており、その際にトライアルをご紹介いただきましたので、少し触ってみました。PractiTestの紹介っぽさを装いつつ、テスト管理ツールに対する自分の考え方アッピールの場とします。

テスト管理ツールとは

 ソースコード管理、ビルド、インシデント管理といった、ソフトウェア開発の様々なアクティビティが専用のWebアプリケーションで行われる中、いまいち泥臭い「管理」から抜けきれないのが、テスト。特に手動テストです。
 いわゆる「テスト管理ツール」と呼ばれるアプリケーション群は、テストケースそのものや、テストケース実行、それに紐づくインシデントの管理をサポートするためのツールです。

 PractiTestもテスト管理ツールの一つで、公式サイトでは、「テストプロセス全体を管理する事のできる、総合テスト管理ツール」とあります。 www.practitest.com

 わたしはこれまで、国内で比較的よく知られているTestLinkと、JaSST'17で適用事例の発表があった、フランス製のSquash TMの二つのテスト管理ツールを使ってきました。その経験も踏まえて、PractiTestを見ていきたいと思います。

 なお、テスト管理ツールに関するわたしの考えについては、こちらにまとめています。

www.kzsuzuki.com

トライアルアカウントの作成

 必要事項を送るとメールが送られてきて、トライアルアカウントの作成ができます。

f:id:kz_suzuki:20191122162551p:plain
電話番号は必須にしないで~
f:id:kz_suzuki:20191122162705p:plain
「QA または ITディレクター」・・・?

 届いたメールのリンクから、トライアルを開始できます。するとすぐに、簡単なチュートリアルが始まります。

f:id:kz_suzuki:20191122162754p:plain
1分間チュートリアル

 テスト管理ツールの一般的な構造を理解している人にとっては当たり前の内容なのですが、そうでなければ「初めに何をすればいいか」から迷うかもしれません。右下にはチャットボックスがあり、簡単に問い合わせが行えるもよう。YouTube動画も埋め込まれたりしており、とにかく「よくわからないから止めよう」とならないための工夫が随所に施されています。

 ログインすると、最初のダッシュボード。まだ何も登録していないので、真っ白です。後で戻ってきましょう。

f:id:kz_suzuki:20191122163149p:plain
ダッシュボード

テストケース作成

 一般的なテストケース管理ツールが想定する理想的な流れだと、まず要件があり、その要件に関連づいたテストケースがあり、そのテストケースを個々の開発に割り当てていくということになるのですが、一番わかりやすいテストケースの作成からやってみましょう。

 Test Library(テストライブラリ)の画面では、管理対象となるテストケースの一覧が表示されます。まだ何も登録されていません。
 なおPractiTestでは、「Test Case」という用語は使っておらず、「Test」で統一しているようです。が、この記事では「テストケース」と呼ぶことにします。

f:id:kz_suzuki:20191122163431p:plain
このシンプルな構成と色遣いはプロっぽくて好き

 左にはフィルタ。よく使いそうなフィルタ、たとえば「自分に割り当てられたテストケース」などが、はじめから用意されています。カスタムフィルタも作成可能です。

 それでは、「New Test」ボタンからテストケースの作成を始めます。全体画面はこんな感じ。

f:id:kz_suzuki:20191122163719p:plain

 テストケースの情報は、3つのタブに分かれています。

「一般」タブ

 「一般」には、テストケースの概要・属性を入力してきます。テストケースの名前、説明、作成ステータス、タグ、添付ファイルといったものです。
 作成ステータスは地味に大事な情報で、そのテストケースをすでに実行に割り当てていいなら「Ready」、まだ作成中なら「Draft」、修正待ちなら「To Repair」、すでに使わなくなったものであれば「Obsolete」としていきます。

 タグは、なぜ必要なのでしょうか。
 TestLinkでは、テストケースをツリー構造で管理します。ですが現実には、テストケースをツリー構造で管理するのは困難です。
 たとえば「機能」でツリーを作るとして、ルート直下に「機能A」「機能B」とぶら下げたとすると、「機能Aと機能Bの競合のテスト」はどこにおくんだ?という問題にすぐぶち当たります。
 解決策は、「ツリーじゃなくてタグを使う」です。「機能A」「機能B」というタグを付ければいいのです。機能Aと機能B、どちらでフィルタしても、そのテストケースはヒットします。

 このあたりの思いは、以下の資料のスライドP.28あたりにあります。

www.slideshare.net

 タグは自由にいくつも付与することができるので便利ですが、いい加減に付けると検索の効果を発揮できなくなってしまいます。運用ルールをある程度決めておかないといけませんね。

 その右には、カスタムフィールドの追加ボタンがあります。組織それぞれでテストケースに付与したい属性があるので、これは嬉しい。
 たとえば、「品質特性」というフィールドを作って、値に「性能」を入れておけば、性能テストをさくっとフィルタできたりしますね(手動で性能テストをやるかは別として・・・)。
 ただこれも、「独自カスタマイズおじさん」が登場すると運用がぐちゃぐちゃになりかねないので、必要最低限のものに絞るのがいいと思います。

f:id:kz_suzuki:20191122164627p:plain

「ステップ」タブ

 次に「Steps」のタブです。名前の通り、テストのステップを入力していきます。

f:id:kz_suzuki:20191122164717p:plain

 ステップの概要、操作内容、期待値を書くことになります。
 操作内容がシンプルであれば、概要と操作内容が似たものになりますが、複雑になると違ってきます。記載の粒度をどのあたりにするかはチームで決めておくのがよいでしょう。
 たとえば、テスト対象ソフトウェア(SUT)への精通度合いをベースに考えるとわかりやすいです。

  • 概要だけ読めば、テストケースの全体の流れが理解できる
  • SUTをよく知っている人は、概要だけでテストケース実行ができる
  • SUTに不慣れな人でも、操作内容を読むことでテストケース実行ができる

という基準を満たしているかを考えながら書いていく、というのが一つの考え方になるでしょう。

 手順の並び替えはこのような画面で行います。

 

f:id:kz_suzuki:20191122165023p:plain
並び替えは別画面で行う

 ここに現れるのは概要の部分。よって、概要で「何をするのか」を必要十分に書いておかないと、並び順の妥当性がわからず、画面を行き来することになってしまいます。やはり、概要だけで流れが読み取れるかを意識するのがよいでしょう。

 もう一つの考え方として、キーワード駆動テストの実装の意識するというのもアリかもしれません。

  • 概要だけ読めば、テストケースの全体の流れが理解できる
  • 概要では、将来キーワードにできそうな一般的な表現で記載する。たとえば「画面Xを開く」はいろんな個所で使いそうです。
  • 操作内容では、キーワードを実装する際に必要になる細かい操作内容を書き下す。

 こうしておけば、キーワードの抽出と実装がやりやすくなります。

「トレーサビリティ」タブ

 次に、「トレーサビリティ」タブを見てみましょう。

f:id:kz_suzuki:20191122165339p:plain

 まず、わかりづらい「Instances」(インスタンス)から。
 「インスタンス」という言葉を使うかどうかは別にして、このような概念はテスト管理ツールに共通的なものです。PractiTestの考える論理関係は、以下のように示されています。

f:id:kz_suzuki:20191122165425p:plain
PractiTestの論理構造

 先ほど追加したテストケースは、「TEST X」と表現されています。そのテストケースを入れる箱がテストライブラリです。すべてのテストケースは、テストライブラリの中にあります
 ですが、そのすべてのテストケースを、毎回の開発で実行するわけではありませんよね。テストライブラリの中にあるテストケースを選択するわけです。実行対象として選択されたテストケースは、インスタンスと呼ばれますインスタンスの集まりがテストセットです

 上の図では、「TEST SET Y」の下に、二つのインスタンス「Y:1」と「Y:2」がぶら下がっています。インスタンスは、テストセットと、ライブラリ内のテストケース、それぞれに関連づいているため、トレースできることになります。

 あとの二つは特に難しくないですね。Linked Issuesは、そのテストケースに関連づいたイシュー。Linked Requirementは、そのテストケースの親となる要件です。
 テストケース作成直後は、まだテストセットへの割り当てもなく、実行もされていないため、トレースできる相手がいません。

 では、一通り入力したうえで登録してみます。

f:id:kz_suzuki:20191122165804p:plain

 ライブラリに戻ってきました。今登録されたテストケースが、一覧に表示されていることがわかります。  テストケースの再編集は、「Name」から行うことができます。テストケース自体のコピー・削除は、右のアイコンからです。

 すでに長くなってしまってので、「その2」に続きます・・・。 www.kzsuzuki.com

判定器の「よしあし」に関するメトリクスを確認しておく - その1

 JaSST'19 TokyoでのQA4AIの発表資料『AIプロダクトに対する品質保証の基本的考え方』に、Model Robustnessのキーワードとして「正答率、適合率、再現率、F値」とあります。

www.slideshare.net

 このあたり、自分の中でちゃんと咀嚼できていないなあと思っていたので、整理してみました。
 まあすでにいろんなページがあるので何を書いても再発明以下になってしまうのですが、自分でやってみないと覚えないんですよね・・・。

判断と誤判断

 たとえば量産品の検査において、不良品を検出したいとします。
 この場合、モノが不良品であれば「陽性」(Positive)、良品であれば「陰性」(Negative)ということになります*1

 もちろん、検査における良/不良の判断と、実際のモノの良/不良が一致するとは限りません。誤判断ってやつです。
 たとえば実際には不良品なのに、検査では良品(陰性)だと判断してしまった場合、これを「偽陰性」(False Negative)と呼びます。逆に、良品を不良品と誤判定するのが、「偽陽性」(False Positive)です。

 表にすると、以下のようになります。これを混合行列(Confusion Matrix)と呼びます*2
 なんというか、Googleっぽくなってしまった・・・。

f:id:kz_suzuki:20191108132836p:plain
図1: 混合行列

 日本語限定のネタですが、

  • 偽陽性: 悪くないものを「悪いのでは!」と誤ってしまうことで、「α過誤」ともいうことから「わてものの誤り」
  • 偽陰性: 悪いものなのに「まあ大丈夫だろう・・・」と誤ってしまうことで、「β過誤」ともいうことから「んやりものの誤り」、

 と称することもあるようです。けっこう見事な言葉遊びですよね。

判定のよしあし

 混合行列上の各要素を組み合わせることで、判定の「よしあし」を定量化することができます。

  • 正答率 (Accuracy): 判定がどれだけ正しいか。正確度。
  • 適合率 (Precision): 陽性判定がどれだけ正しいか*3。適合度。精度。
  • 再現率 (Recall): 実際の陽性をどれだけ拾えているか。感度。
  • 特異度 (Specificity): 実際の陰性をどれだけ拾えているか。

 例として、混合行列の各値が以下のようになっていたとしましょう。

f:id:kz_suzuki:20191108134125p:plain
図2: 混合行列の具体例

 ちょっとゴチャゴチャしていますが・・・
 全数100件に対し、良品(N)が90、不良品(P)が10。これに対し、不良品予想を9、良品予想を91したところ、正解(T)が97で、うち真陽性(TP)が8、真陰性(TN)が89。不正解(F)は3で、うち偽陽性(FP)が1、偽陰性(FN)が2、という読み方になります。

正答率 (Accuracy)

(TP+TN) / (TP+TN+FP+FP)
真 / 全数

 これは単純に、全判定のうち、正解であった割合です。陽性は陽性と、陰性は陰性と判断できているか。混合行列でいう True であるか。というメトリクスです。
 上の例だと、正答率は97.0%になっています。

適合率 (Precision)

TP / (TP+FP)
真陽性 / 予想陽性

 判断が「陽性」であるもののうち、実際に陽性であった(True Positive)割合です。
 検査者が「あわてもの」であった場合、過剰に「これは不良品!」と判断することにより、この値は低くなります。
 上の例だと、適合率は88.9%になっています。  

再現率 (Recall)

TP / (TP+FN)
真陽性 / 実際陽性

 実際に陽性であるもののうち、判断でも陽性とした(True Positive)割合です。感度 (Sensitivity) という呼び方もあります。
 検査者が「ぼんやりもの」であった場合、「まあ、これは大丈夫だろ・・」と判断することにより、この値は低くなります。
 上の例だと、適合率は80.0%になっています。

特異度 (Specificity)

TN / (FP+TN)
TN / 実Negative

 再現率の逆で、実際に陰性であるもののうち、判断でも陰性(True Negative)とした割合です。

なぜいろいろなメトリクスが必要なのか

正答率のあやしさ

 陽性になる確率が1%程度しかない事象を考えてみましょう。陽性=1、陰性=99とします。
 この性質があらかじめわかっているとき、判定者は何も考えずに「陰性」としておけば、高い正答率を達成できます。

ケース1
・陽性と判定して実際に陽性だったケース (TP): 0
・陽性と判定したが実際には陰性だったケース (FP): 1 ← あわてもの
・陰性と判定したが実際には陽性だったケース (FN): 1 ← ぼんやりもの
・陰性と判定して実際に陰性だったケース (TN): 98

 この場合、正答率は98.0%という高い値になります。
 「だいたい陰性だろう」とわかっているので、適当に陰性判定していれば、数値が高くなるのですね。

 一方で適合率と再現率は0%です。
 現実の陽性をまったく拾えていないし、陽性の判定もまったく当たっていないからです。

適合率と再現率の関係

 では、適合率と再現率はどういう関係にあるのでしょうか。
 13%が陽性、87%が陰性というケースを考えてみます。

ケース2
・陽性と判定して実際に陽性だったケース (TP): 12
・陽性と判定したが実際には陰性だったケース (FP): 43 ← あわてもの
・陰性と判定したが実際には陽性だったケース (FN): 1 ← ぼんやりもの
・陰性と判定して実際に陰性だったケース (TN): 44

 半分くらい間違っている(F)ので、正答率は56.0%に留まります。陽性判定の信頼性を意味する適合率も、FPが多いのでわずか21.8%です。
 一方、陽性をどれだけ拾えるかを意味する再現率は、92.3%。分布的には陽性が13%しかないものに対し、半分以上で陽性判定を下しているので、実際の陽性もそれなりにカバーできてしまうということです。
 この「あわてもの」をやられると、病院なら再検査、工場なら単価上昇につながってしまいますね。

 もう一つ、別のケースです。

ケース3
・陽性と判定して実際に陽性だったケース (TP): 1
・陽性と判定したが実際には陰性だったケース (FP): 0 ← あわてもの
・陰性と判定したが実際には陽性だったケース (FN): 12 ← ぼんやりもの
・陰性と判定して実際に陰性だったケース (TN): 87

 この人はケース1と同様に陰性に多くを賭けている分、正答率は88.0%。一度しか出していない陽性判定が当たったので、適合率は100.0%です!
 一方で本当の陽性をたくさん見逃しているので、再現率はたったの7.7%です。  この「ぼんやり」をやられると、病院なら病気の見逃し、工場ならクレーム多発につながってしまいます。

 陽性の確率が低い場合には、ざっくり以下のようになります。

  • とにかく陽性判定しておけば(あわてもの)、本当の陽性も拾えるので再現率が高くなる。でも多くが偽陽性なので、適合率は低くなる。
  • とにかく陰性判定しておけば(ぼんやりもの)、実際にも陰性が多いので適合率は高くなる。でも陽性を拾えなくなるので、再現率は低くなる。

あわてとうっかりのバランスをとる

 ということで、あわてものとぼんやりもののバランスを取る必要があります。
 適合率と再現率の調和平均を取ったのが、F1スコアです。
 横軸に適合率、縦軸に再現率をとって、バブルの大きさにF1スコアを当てると、こんなイメージになります。

f:id:kz_suzuki:20191108142339p:plain
F1スコアのイメージ

 適合率、再現率がともに上昇すればF1スコアも大きくなっていきます。一方、適合率を上げるために再現率を犠牲にしたり、その逆をしたりするより、両者のバランスを取る方がF1スコアが大きくなることが見てとれます。

 ケース2のF1スコアは0.35、ケース3では0.14という低さです。

ケース4
陽性と判定して実際に陽性だったケース (TP): 8
陽性と判定したが実際には陰性だったケース (FP): 6 ← あわてもの
陰性と判定したが実際には陽性だったケース (FN): 5 ← ぼんやりもの
陰性と判定して実際に陰性だったケース (TN): 81

 このケースでは、正答率は89.0%とまあまあですが、適合率は57.1%、再現率は61.5%とともにいまいち。ただバランスがとれているのでFスコアは0.59と、先の2つよりはましになっています。

まとめ

 「判定の適切さ」とひとことで言っても、何を重視するかによって使うべきメトリクスが異なることがわかりました。
 あわてものを少なくすることが是なら適合率、ぼんやりものを少なくするのが是なら再現率。
 そのバランスを取りたいならFスコアです。*4

 これにて、『AIプロダクトに対する品質保証の基本的考え方』の1スライドの1行分の勉強が終わりました。

参考

www.kdnuggets.com

*1:「陽」のイメージからすると良品になりそうですが、見つけたいものが不良品なので、この場合は逆になっています。

*2:「判断」という言葉と「予想」という言葉が混合してしまって申し訳ない・・・

*3:「陰性判定がどれだけ正しいか」もありそうですが、見当たらない。

*4:2つに重みを付ける「Fβ」というメトリクスもあるようです。

テストを進めることで品質に何が起きているのか(思考実験)

 この「ソフトウェアテスト Advent Calendar 2019」*1の、-34日目のエントリーです。
 単なる思い付きの思考実験ですが、アイデアいただけると嬉しいです。

テスト実行と品質をどのように描くか

 ソフトウェアテストを、「対象とするソフトウェアの品質レベルを推定し、不確実性を小さくしていく活動」と捉えてみます。
 テストを始める前は、どの程度の品質なのかについての手がかりがなく、「0から100のいずれかの範囲にある」*2ということしかわかりません。平均値である「50」が、「この辺かなあ」と思える品質ですが、不確実性が大きすぎてあまり意味がありません。

f:id:kz_suzuki:20191027064729p:plain

 テストケースが1つパスすると、下限が上昇します。これは2つのことを意味しています。

f:id:kz_suzuki:20191027064732p:plain

  1. 「品質についての不確実性が減った」ということ。
  2. 「一番確からしい品質のレベルが上がった」ということ。

 上の図では、1個のテストケースがpassすると、下限が5だけ上がっています。すると、実行前には0から100までのどこかだろうと考えていた品質の範囲が、5から100までのどこかだと絞られたことになります。これがa.です。
 またそれに伴い、平均は50から52.5に上がっており、期待していい品質が上がったことになります。これがb.です。

 逆にテストが1つfailすると、上限が下降します。

★図3f:id:kz_suzuki:20191027064704p:plain

 これも2つのことを意味しています。

  1. 「品質についての不確実性が減った」ということ。
  2. 「一番確からしい品質のレベルが下がった」ということ。

 このモデルのポイントは、次の2点です。

  1. 結果がpassであればfailであれ、テストを進めれば不確実性は減る
  2. テストを進めることで上がるのは「品質」それ自体ではなく、「期待できる品質」である。

 テストをすればするほど品質に対する確信度合いが深まっていくというのは、まあ直観に合うでしょう。また、テスト自体が品質を上げてくれるわけではないという事実も、モデルに反映されています。

モデルはどのような性質を持つべきか

 さて、このモデルはあまりに単純です。
 まず、最初のテストケース実行により下限を押し上げた「5」とは何なのでしょうか。この属性に仮に、「テストケースの重み」という名前を付けてみましょう。「重み」はいくつかの特徴を持ちます。

(1)テストケースによって重みが異なる

 いわゆる「ど真ん中」「ハッピーパス」「正常系」と呼ばれるような、テストの最初にとりあえずやってみるようなテストケース。このようなテストケースがfailすれば、上限は大きく下がるでしょう。一方、非常にレアな、あるいは極端な条件でのテストケースがfailしても、上限はそれほど下げなくてもいいでしょう。
 このように、テストケースごとに相対的な重みは異なると考えられます。

(2)passしたときとfailしたときで重みが異なる

 引き続き「ど真ん中」テストケースを考えてみます。
 このテストケースの実行結果がfailだと驚きですが、passは当たり前。なので、品質に対する確信をそれほど上げてはくれません。上限の下降は小さいでしょう。つまり、passとfailでは不確実性を狭めるのに寄与する度合いが違います

 下の図では、当たり前テストケースがfailしたため、期待が大きく下がり、「より確実に品質が悪い」と判断されうる状態になっています。

f:id:kz_suzuki:20191027064709p:plain

(3)テストケース実施のタイミングで重みが変わる

 たとえば境界値分析を行い、自然数の1~10が1つのクラスだったします。境界にある「1」と「10」のテストに加え、inの値として「5」をテストしたとします。 (1)で述べたように、「1」「10」のpassに比べて、「5」のpassの重みは小さいでしょう。

 では「6」はどうでしょうか。「5」がpassした後の「6」のpassには、品質に対する情報がさらに小さいといえます。ですが、「5」より先に「6」がpassしていたら? この場合は、「5」がpassするかは未知なのですから、重みは相対的に大きくなります。
 もちろん、「1」と「10」がpassした後に「5」や「6」がfailになった場合は、(2)により大きな重みが付けられます。境界を見誤っている可能性を考える必要があります。

 以上を考えていくと、この「重み」というのは、「そのタイミングでテストケースを実行した結果得られる情報量」であることがわかります。「エントロピー」というのかもしれません。

不確実性は常に下がっていくのか

 上述の(3)の例をもう少し考えてみましょう。

 あるテストケースがpassした後に、もう一度同じテストケースを実行してpassだった場合、情報量はゼロなのでしょうか。
 そうではないでしょう。完全に同じ条件を再現することができない以上、同じテストケースで同じ結果を得られることは保証されないからです。つまり「厳密には別のテストを実行した」ことになり、わずかながら情報は得られると考えられます。同じ理屈で、リグレッションテストのように繰り返し行うテストは、実行のたびに得られる情報が逓減していく*3ことになります。

 下の図では、5回連続「同じ」テストケースがpassしたことによる変化を表現しています。下限の上昇具合が緩やかになっています。

f:id:kz_suzuki:20191027064713p:plain

 一方、failするとどうなるでしょう。
 まず、間歇性の欠陥が潜在している可能性が出てきます。
 と同時に、過去のテスト実行が信頼できないものであった可能性も考慮する必要があります。正しい条件でテストできていなかったとか、結果を適切に判断していなかったというケースです。

 すると、テストを進めると小さくなっていくはずだった不確実性が大きくなってしまいます。
 モデル的には下の図のように、上限だけでなく下限も下げて表現することになるでしょうか。

f:id:kz_suzuki:20191027064719p:plain

テストが終わるとどうなるのか

 テストケースをすべて実行し、passだった場合、不確実性はゼロに、上下限はともに100になるのでしょうか。
 これは「完全な品質であることを100%確信できる状態」を意味します。そうはならないでしょう。これは「ソフトウェアテストの7原則」の2つ目、

全数テストは不可能

によるものです。

 100%の確信のためには、100%のテストケースが必要になります。考慮されていないテストケースもあれば、考慮したうえで実行を省いているテストケースもあるでしょう。よって、計画したすべてのテストケースを実行し終えても、有限の不確実性が残ります。

f:id:kz_suzuki:20191027064723p:plain

 つまり、考えられるテストをすべて行ったとしても不確実性は残り、期待できる品質も100点満点にはならない、というモデルになるでしょう。上限を100から始めている点も誤っているといえます。

 ソフトウェアの修正において、「影響範囲を見誤ってテストが漏れた」というのも、この例になるでしょう。
 当初必要だと考えていたテストが進むにつれ、期待できる品質は上がり、不確実性も減っているように見えます。しかし実際には、重要なテストケース、つまり「不確実性を減らすために大きな情報をもたらすテストケース」が漏れており、期待できる品質はもっと低く、不確実性も大きかったということです。

まとめ

 テスト実行の推移と品質の関係についてのシンプルなモデルの考察を行ってみました。
 ただ、今ふと思ったのですが、「新しい情報を得ることで確率が変動する」って、ベイズ推定で考えるべきテーマだったのか・・・? 今さら・・・?

 明日10/28(月)は、アドベントカレンダー -33日目です!

*1:なぜかリンクが見つかりません。

*2:この時点で、品質0とか品質100が何を意味するのかという問題がある。上と下を無限大にすることもできるかもしれないけれど、「無限大の品質」はさらに意味不明である。

*3:テスト対象が変化していない場合。

AI/DLシステムの説明可能性と組み合わせテストの関係

はじめに

 辰巳敬三さんが先日、このようなツイートをされていました。

 「An Application of Combinatotial Methods for Explainability in Artificial Intelligence and Machine Learning」(人工知能や機械学習の説明可能性に対する組み合わせメソッドの適用)。

csrc.nist.gov

 AIを組み込んだシステムの説明可能性(explainability)は、よく言及されるところです。5月に発行された『AIプロダクト品質保証ガイドライン』では、説明可能性について以下のように述べています。

 説明可能性・解釈性(Explainability/Interpretability)は、システムからの出力を用いる人間が、出力を得る際に用いられた判断基準(モデルが学習した規則性)について把握することができる程度を表す〔ガニング2016〕。要求や振る舞いを明確に形式知として書き出せない場合でも機能を実現できることが機械学習の強みである。しかし、人間が出力を参考にして意思決定を行う場合など、アプリケーションによっては説明可能性・解釈性が必要となる。

 説明可能性をもったAIは、「XAI」(eXplainable AI)と呼ばれています。
 システムがどのような推論を経て最終的な判断・結論を得たかを考える説明可能性の話と、ソフトウェアテスト技法の文脈で現れる組み合わせテスト。一体何が関係あるのかと思って、紹介いただいた資料を読んでみました。なお本資料はホワイトペーパーのドラフトという位置づけです。

概要

 もしかするとこちらを見れば一瞬で理解できるのかもしれませんが・・・。

csrc.nist.gov

 以下、ポイントを要約してみます。誤読あればご指摘ください。

Abstract

  • 組み合わせテストにおける欠陥の所在特定(location)の方法を、AIによる判断の説明に適用することができる。
  • 適用するのは、対象の特性(characteristics)に基づいてメンバーシップを決定する、分類(classification)の問題。
  • メンバーであれば持っているが、非メンバーは持っていない特性の組み合わせを特定する。

Background

  • AIの正確さと説明可能性はトレードオフの関係にある。
    • たとえばCNNは現在もっとも正確な方法であるが、内部計算についての説明には欠けている。ドメインには詳しいがAI/MLに詳しくない人に、結論を理解してもらう必要がある。
    • ルールベースのシステムは結論を理解しやすい。たとえば、「もし症状Aが、症状Bか症状Cと一緒に現れていたら、診断はXである」というロジックがわかる。一方、正確さでは劣る。
  • 機械学習における分類の問題と、ソフトウェアの組み合わせテストにおける欠陥の所在特定は、ともに非常に多くのプロパティや値の組み合わせを特定することが目的。
    • 組み合わせテストにおいては、SUTの故障のトリガーとなる組み合わせ
    • AI/DLシステムにおいては、ある結論を導き出す組み合わせ
  • AI/DLシステムが結論を導く変数の組み合わせを特定するために、組み合わせテストのツールを活用できる。

Fault Location

  • 組み合わせテストの難しさは、faultのトリガーになる要素が1つでないことに起因する。どの値の組み合わせが故障につながるのかを特定したい。
    • 15個あるパラメタから4個の組み合わせをとると、1,365パターンになる。
  • 従来のパラダイムとしては、「成功したテストには現れず、失敗したテストに現れる組み合わせ」を探す。前者に現れる組み合わせは明らかにトリガーでない。
  • ComXAIというツールを使って、クラスに分類されなかったデータにおける、t-way組み合わせを分析する。

Examples

  • 「爬虫類かそうでないか」の分類を、16個の属性から判定する例。2つの属性の組み合わせ(2-way)は120、3つの属性の組み合わせ(3-way)は560種類ある。
  • たとえば属性の1つ「歯があるかないか」が取りうる値には「歯がある」「歯がない」があるが、これらの値はともに、「爬虫類」クラスに分類されたものにも、「非爬虫類」クラスに分類されたものにも現れる。つまり、属性「歯があるかないか」は、単独ではクラスを識別することができない
  • 次に2-way。「歯がある」「足の数が4」という組み合わせは、「非爬虫類」クラスのデータのうち2.1%に現れる組み合わせ。よってこれでもクラスの識別のための属性群としてはまだ弱い。
  • 3-wayになると、「爬虫類」クラスのデータには現れるが「非爬虫類」クラスのデータにはまったく現れない値の組み合わせが出てくる。
    • たとえば、「水棲である」かつ「歯がある」かつ「足の数が4」。
  • これらの組み合わせは、「爬虫類である」と判断するためのルールと見なすことができる。

Discussion

  • 「あるクラスには現れるが、それ以外のクラスには現れない」パラメタ値の組み合わせを見つけてルールセットとすることで、AI/DLシステムによる結論に、直感的な説明を与えることができる。
  • 結論の説明だけでなく、DLの分類モデルのアルゴリズム実装にもこのアプローチを使うことができるかもしれない。
  • 小規模な問題であっても組み合わせパターンは膨大になるため、得られたルールセットは巨大なものになりうる。また過学習も問題になる。

所感

 AIシステムと組み合わせテストの関係がまったく想像つかなかったのですが、読んでみるとシンプルなアイデアで、面白いと思いました。ただ、いくつかよくわからない部分もあります*1

(1)どちらにも現れない組み合わせ

 上の例を借りると、「非爬虫類のデータには現れない値の組み合わせは逆に、爬虫類であると判定するためのルールと見なすことができる」というアイデアです。でもたとえば「足が13本である」というデータは、「爬虫類」側にもない(または希少)なので、「爬虫類」であるとする判定には使うべきでないでしょう。「爬虫類クラスのデータには十分に現れている組み合わせ」であることも必要条件なのではないかと思いました*2

 「足が13本」のような極端な話でなくても、「爬虫類」クラスのデータにたまたま現れなかった値の組み合わせをもって「非爬虫類」クラスに分類するためのルールとされても困る。これが Discussion にあった過学習の問題でしょうか。

(2)用意されたデータ

 「画像を見てどの動物かを分類する」というシステムにおいて、「歯がある」「足が4本ある」のような属性と値のセットがデータとして与えられるのは普通なのかな?と思いました。
 このペーパーでは、爬虫類か否かの分類の例の他、二酸化炭素濃度などのセンサーデータから部屋が使用中か空きかを判定する例と、リンパ系造影法(lymphography)の画像から病理診断を行う例が紹介されています。ともに、分類を行うための属性と値が明確。属性自体が不明の場合はまた話が別ですね。

(3)予想もしなかった組み合わせ

 ルール導出の結果、「なるほど」と思えるものだけでなく、「はあ!?」となる「ルール」が現れる可能性があります。一見、まったく無関係に思える組み合わせが、結論に関与しているようなケースです。これにどう対応するのかというのが疑問でした。
 これは Conclusion and Research Direction の最後にちらっと書かれています。

 クラスを特徴づける要素の組み合わせがユーザの期待に合致しないようであれば、導出されたクラスに欠陥があるのかもしれない。あるいは、予期していなかった関係が結果に影響しているのかもしれない。いずれにしても、モデルをより理解することにつながる。

 得られた組み合わせ・ルールを必ずしも真と見なすわけではなく、モデルの見直しや、未知の要因の発見につなげるのですね。

*1:ペーパーの問題ではなく、読む側の問題だと思われますが・・・

*2:ComXAIで計算しているのは、「爬虫類」のデータに現れた組み合わせに絞っているのかもしれない。