![]() |
![]() |
![]() |
![]() |
![]() |
これは、テキストの質問と回答の取得に使用する多言語対応のユニバーサルセンテンスエンコーダー Q&A モジュールを使用して、モデルの question_encoder と response_encoder を説明する実演です。デモ用データセットとして SQuAD 段落の文章を使用します。各文章とそのコンテキスト(文章の周辺にあるテキスト)は、response_encoder を使って高次元埋め込みにエンコードされており、質問と回答の取得に使用できるよう、埋め込みは simpleneighbors ライブラリを使用して構築されたインデックスに保存されています。
取得時、SQuAD データセットからランダムな質問が選択されて question_encoder で高次元埋め込みにエンコードされ、simpleneighbors インデックスをクエリすると、セマンティック空間の最近傍のリストが返されます。
その他のモデル
現在ホストされているテキスト埋め込みモデルはこちらを、SQuAD でもトレーニングされたすべてのモデルはこちらをご覧ください。
セットアップ
Setup Environment
%%capture
# Install the latest Tensorflow version.
!pip install -q tensorflow_text
!pip install -q simpleneighbors[annoy]
!pip install -q nltk
!pip install -q tqdm
Setup common imports and functions
import json
import nltk
import os
import pprint
import random
import simpleneighbors
import urllib
from IPython.display import HTML, display
from tqdm.notebook import tqdm
import tensorflow.compat.v2 as tf
import tensorflow_hub as hub
from tensorflow_text import SentencepieceTokenizer
nltk.download('punkt')
def download_squad(url):
return json.load(urllib.request.urlopen(url))
def extract_sentences_from_squad_json(squad):
all_sentences = []
for data in squad['data']:
for paragraph in data['paragraphs']:
sentences = nltk.tokenize.sent_tokenize(paragraph['context'])
all_sentences.extend(zip(sentences, [paragraph['context']] * len(sentences)))
return list(set(all_sentences)) # remove duplicates
def extract_questions_from_squad_json(squad):
questions = []
for data in squad['data']:
for paragraph in data['paragraphs']:
for qas in paragraph['qas']:
if qas['answers']:
questions.append((qas['question'], qas['answers'][0]['text']))
return list(set(questions))
def output_with_highlight(text, highlight):
output = "<li> "
i = text.find(highlight)
while True:
if i == -1:
output += text
break
output += text[0:i]
output += '<b>'+text[i:i+len(highlight)]+'</b>'
text = text[i+len(highlight):]
i = text.find(highlight)
return output + "</li>\n"
def display_nearest_neighbors(query_text, answer_text=None):
query_embedding = model.signatures['question_encoder'](tf.constant([query_text]))['outputs'][0]
search_results = index.nearest(query_embedding, n=num_results)
if answer_text:
result_md = '''
<p>Random Question from SQuAD:</p>
<p> <b>%s</b></p>
<p>Answer:</p>
<p> <b>%s</b></p>
''' % (query_text , answer_text)
else:
result_md = '''
<p>Question:</p>
<p> <b>%s</b></p>
''' % query_text
result_md += '''
<p>Retrieved sentences :
<ol>
'''
if answer_text:
for s in search_results:
result_md += output_with_highlight(s, answer_text)
else:
for s in search_results:
result_md += '<li>' + s + '</li>\n'
result_md += "</ol>"
display(HTML(result_md))
[nltk_data] Downloading package punkt to /home/kbuilder/nltk_data... [nltk_data] Unzipping tokenizers/punkt.zip.
次のコードブロックを実行して、SQuAD データセットを次のように抽出します。
- sentences: (text, context) のタプル式のリストです。SQuAD データセットの各段落は nltk ライブラリを使って文章ごとに分割され、その文章と段落のテキストによって (text, context) タプル式を形成します。
- questions: (question, answer) タプル式のリストです。
注意: 以下の squad_url を選択すると、この実演を使用して、SQuAD の train データセットまたはより小規模な dev データセット(1.1 または 2.0)のインデックスを作成できます。
Download and extract SQuAD data
squad_url = 'https://rajpurkar.github.io/SQuAD-explorer/dataset/dev-v1.1.json'
squad_json = download_squad(squad_url)
sentences = extract_sentences_from_squad_json(squad_json)
questions = extract_questions_from_squad_json(squad_json)
print("%s sentences, %s questions extracted from SQuAD %s" % (len(sentences), len(questions), squad_url))
print("\nExample sentence and context:\n")
sentence = random.choice(sentences)
print("sentence:\n")
pprint.pprint(sentence[0])
print("\ncontext:\n")
pprint.pprint(sentence[1])
print()
10452 sentences, 10552 questions extracted from SQuAD https://rajpurkar.github.io/SQuAD-explorer/dataset/dev-v1.1.json Example sentence and context: sentence: ('Such experiments demonstrate the crucial properties that forces are additive ' 'vector quantities: they have magnitude and direction.') context: ('Historically, forces were first quantitatively investigated in conditions of ' 'static equilibrium where several forces canceled each other out. Such ' 'experiments demonstrate the crucial properties that forces are additive ' 'vector quantities: they have magnitude and direction. When two forces act on ' 'a point particle, the resulting force, the resultant (also called the net ' 'force), can be determined by following the parallelogram rule of vector ' 'addition: the addition of two vectors represented by sides of a ' 'parallelogram, gives an equivalent resultant vector that is equal in ' 'magnitude and direction to the transversal of the parallelogram. The ' 'magnitude of the resultant varies from the difference of the magnitudes of ' 'the two forces to their sum, depending on the angle between their lines of ' 'action. However, if the forces are acting on an extended body, their ' 'respective lines of application must also be specified in order to account ' 'for their effects on the motion of the body.')
次のコードブロックは、Univeral Encoder Multilingual Q&A モデルの question_encoder と response_encoder シグネチャを使用して、TensorFlow グラフ g と session をセットアップします。
Load model from tensorflow hub
module_url = "https://tfhub.dev/google/universal-sentence-encoder-multilingual-qa/3"
model = hub.load(module_url)
次のコードブロックは、response_encoder を使用して、すべてのテキストの埋め込みを計算し、タプルのコンテキストを形成し、simpleneighbors インデックスに格納します。
Compute embeddings and build simpleneighbors index
batch_size = 100
encodings = model.signatures['response_encoder'](
input=tf.constant([sentences[0][0]]),
context=tf.constant([sentences[0][1]]))
index = simpleneighbors.SimpleNeighbors(
len(encodings['outputs'][0]), metric='angular')
print('Computing embeddings for %s sentences' % len(sentences))
slices = zip(*(iter(sentences),) * batch_size)
num_batches = int(len(sentences) / batch_size)
for s in tqdm(slices, total=num_batches):
response_batch = list([r for r, c in s])
context_batch = list([c for r, c in s])
encodings = model.signatures['response_encoder'](
input=tf.constant(response_batch),
context=tf.constant(context_batch)
)
for batch_index, batch in enumerate(response_batch):
index.add_one(batch, encodings['outputs'][batch_index])
index.build()
print('simpleneighbors index for %s sentences built.' % len(sentences))
Computing embeddings for 10452 sentences 0%| | 0/104 [00:00<?, ?it/s] simpleneighbors index for 10452 sentences built.
取得時、質問は question_encoder でエンコードされ、質問の埋め込みを使って simpleneighbors インデックスがクエリされます。
Retrieve nearest neighbors for a random question from SQuAD
num_results = 25
query = random.choice(questions)
display_nearest_neighbors(query[0], query[1])