מערכת שאלות ותשובות מבוססת בינה מלאכותית

מחבר:
בתאריך:

במדריך נשתמש במנוע החיפוש Elasticsearch ובבינה מלאכותית מבוססת טרנספורמר כדי לענות על שאלות אודות מאמר. Elasticsearch הוא מנוע חיפוש המתמחה באיתור טקסט ואף ניתן לשלבו עם מודלים מסוג טרנספורמרים - המובילים בעולם בתחום עיבוד שפה טבעית NLP.

מערכת בינה מלאכותית עם טרנספורמר ו-elastisearch כדי לשאול שאלות אודות קטע קריאה

את המאמר שנוריד מויקיפדיה נקודד באמצעות מודל מאומן מסוג טרנספורמר לוקטורים שכל אחד מהם מייצג פסקה. את הוקטורים נאחסן במסד נתונים Elasticsearch. נזין את מנוע החיפוש בשאלה ונקבל כמה תשובות אפשריות. השאלה תקודד לוקטור ומנוע החיפוש יאתר מבין הפסקאות המקודדות את חלקי הפסקאות המכילים את היצוגים הוקטוריים הדומים ביותר מהם יפיק המודל את התשובות.

 

מערכת הקבצים

את הפרויקט כתבתי בפייתון. בתוך תיקיית הפרויקט אני מצפה למצוא את ארבעת הקבצים הבאים המסודרים לפי סדר הפעולות במדריך:

├── 0_get_data.py
├── 1_store_in_db.py
├── 2_encode_db.py
├── 3_query.py
  1. איסוף המידע
  2. אחסון המידע במסד הנתונים
  3. הפיכת המידע לוקטורים
  4. שואלים את המערכת שאלות

להורדת תיקיית הפרויקט

 

איסוף מידע מויקיפדיה

לויקיפדיה יש מנוע חיפוש המאפשר לאתר מאמרים לפי כותרות. במדריך קודם הסברתי כיצד לגרד את המידע. במדריך זה אאמץ את הקוד כולל כמה שינויים קטנים במטרה לנקות את המידע.

הקוד הבא מגרד מויקיפדיה את המאמר אודות טרנספורמרים, ומאחסן את הטקסט בקובץ transformer.txt אצלנו על המחשב:

0_get_data.py

import requests, re
from bs4 import BeautifulSoup

# Wikipedia links
wikipedia_api_link = "https://en.wikipedia.org/w/api.php?format=json&action=query&list=search&srsearch="

# Create the search URL by adding the search term
search_term = "Transformer (machine learning model)"

url = wikipedia_api_link + search_term

# Get the url from the first article in the search results
r = requests.get(url)

json_output = r.json()

article_title = json_output['query']['search'][0]['title']

article_title = article_title.replace(' ', '_')

wikipedia_link_article = "https://en.wikipedia.org/wiki/" + article_title

# Scrape the HTML content from the page
def request_webpage(url):
  res = requests.get(url)
  try:
    res.raise_for_status()
  except Exception as exc:
    print('There is a problem with the request')
  return res

page = request_webpage(wikipedia_link_article)

bs_page = BeautifulSoup(page.text, features="lxml")

content = bs_page.find("div", {"id": "content"})


# HTML to text
ori_text = content.get_text()

# Sanitize the text
text = re.sub(r'\{.*}', '', ori_text)
text = re.sub(r'\^.*', '', text)

text = text.split('vte')[1]
text_lines = text.split('\n')
text_lines = [line for line in text_lines if len(line) > 20]
text = '\n'.join(text_lines)
text = re.sub(r'\[.*]', '', text)

# Write into a file
with open('./transformer.txt', 'w', encoding='utf-8') as f:
    f.write(text)

 

תחילת העבודה עם סביבת Elasticsearch

אני מניח שהתקנתם את Elasticsearch על המחשב שאיתו אתם עובדים. אם לא אז אפשר להעזר בהנחיות בתיעוד הרשמי.

נפעיל את Elasticsearch:

$ sudo systemctl start elasticsearch
  • זה עובד על לינוקס. לגבי מערכות אחרות תצטרכו להיעזר בתיעוד.

ההמלצה היא לייחד ל- Elasticsearch את פורט 9200. נוודא שאנחנו יכולים לתקשר:

$ curl -XGET localhost:9200

נבחן את בריאות המערכת:

$ curl -XGET localhost:9200/_cluster/health?pretty

הסטטוס צריך להיות ירוק או צהוב.

 

אחסון המידע ב-Elasticsearch

בחלק זה נקרא את המידע מתוך מסמך הטקסט שיצרנו בחלקו הראשון של המדריך, את הפסקאות נהפוך לפריטים במערך אותו נזין ל-Elasticsearch.

נפתח את הקובץ לקריאה, ונהפוך כל פסקה לפריט במערך data:

1_store_in_db.py

import requests

with open('./transformer.txt', 'r', encoding='utf-8') as f:
    data = f.read()

# split into paragraphs
data = data.split('\n')

data = [paragraph for paragraph in data if len(paragraph) > 0]

נפרמט את המערך בהתאם לדרישות ממידע שרוצים להזין ל-Elasticsearch:

1_store_in_db.py

# put the data into a format we can store
data_json = [
    {
        'text': paragraph,
        'meta': {
            'source': 'transformer'
        }
    } for paragraph in data
]

# print(data_json[:3])
  • כדי לקבל את המערך עם המידע בפורמט הרצוי הרצנו לולאה על כל המידע בתוך list comprehension.

נעזר בפלגאין farm-haystack כדי לגשר בין הקוד שאנחנו כותבים בפייתון ובין Elasticsearch. נתקין אותו מהטרמינל:

$ pip install farm-haystack

נקשר את farm-haystack ל-Elasticsearch.

1_store_in_db.py

from haystack.document_store.elasticsearch import ElasticsearchDocumentStore

# connect haystack with Elasticsearch
# creates the index if it doesn't exist
doc_store = ElasticsearchDocumentStore(
  host = 'localhost',
  username='',
  password='',
  index='transformer'
)
  • תתאימו את שם המשתמש והסיסמה למערכת שלכם.
  • אינדקס הוא מסד נתונים ב-Elasticsearch.
  • קראתי לאינדקס 'transformer' בהתאם למידע שאני רוצה להזין לתוכו.
  • במידה והאינדקס לא קיים, Elasticsearch ייצור אותו.

נזין את מערך הפסקאות לאינדקס:

1_store_in_db.py

# write into the database
doc_store.write_documents(data_json)

נשתמש בטרמינל כדי לוודא שהאינדקס קיים:

$ curl -XGET localhost:9200/_cat/indices

התוצאה במקרה שלי:

yellow open transformer 5Z9n8CVjTASRniB2xWBbaw 1 1   60 0  907kb  907kb
  • האינדקס קיים.

כמה מסמכים באינדקס transformer?

$ curl -XGET localhos:9200/transformer/_count
{"count":60,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0}}

 

קידוד הנתונים לווקטרים

כיוון שטרנספורמרים לא יודעים לעבוד עם טקסט ישירות אנחנו צריכים להפוך את הנתונים לוקטורים, ולאחסן מחדש במסמכים (הרשומות של Elasticsearch).

ניצור את הקשר בין פייתון לבין Elasticsearch:

2_encode_db.py

import requests

from haystack.document_store.elasticsearch import ElasticsearchDocumentStore

# connect haystack with Elasticsearch
doc_store = ElasticsearchDocumentStore(
  host = 'localhost',
  username='',
  password='',
  index='transformer'
)

נהפוך כל דוקומנט (פסקה של המאמר שאיחסנו באינדקס) לייצוג הוקטורי באמצעות טרנספורמר מ-hugging face. אפשר להעזר בדף המודלים של hugging face בכתובת huggingface.co/models ולבחור משם שני מודלים של dpr. אחד לקידוד הפסקאות והשני לקידוד השאלה שתיכף נשאל. אני נעזרתי בהמלצות של haystack.

2_encode_db.py

from haystack.retriever.sparse import ElasticsearchRetriever

# encode each passage into a dense vector
# search among representations the most similar to the vector representing the question

# behind the scenes haystack uses the hugging face transformer library
# in huggingface.co/models
# search for dpr models - one for contexts(passages) and the other for questions
retriever = ElasticsearchRetriever(doc_store)

נחליף את הפסקאות במסד הנתונים בייצוג הוקטורי שלהם:

# update the documents in the index with the dpr embeddings
doc_store.update_embeddings(retriever=retriever)

 

שואלים שאלות

בחלק זה נזין שאלות ונצפה לקבל תשובות.

Elasticsearch יעזר במודל מסוג טרנספורמר כדי לקודד את השאלה שנזין לתוכו לוקטור שאותו הוא ישווה לוקטורים שקיימים באינדקס במטרה לאתר את חלקי הפסקאות הדומים ביותר, ולהפיק מהם את התשובות.

ניצור קשר עם Elasticsearch:

3_query.py

from haystack.document_store.elasticsearch import ElasticsearchDocumentStore

# connect haystack with Elasticsearch
# create the index if it doesn't exist
doc_store = ElasticsearchDocumentStore(
  host = 'localhost',
  username='',
  password='',
  index='transformer'
)

from haystack.retriever.sparse import ElasticsearchRetriever

# no need to update the database
retriever = ElasticsearchRetriever(doc_store)

נעזר במודל טרנספורמר שלישי כדי לקרוא את המידע, ונזין את השאלה לתוך pipeline:

3_query.py

# Load a  local model or any of the QA models on
# Hugging Face's model hub (https://huggingface.co/models)
from haystack.reader.farm import FARMReader
reader = FARMReader(model_name_or_path="deepset/roberta-base-squad2", use_gpu=False)

from haystack.pipeline import ExtractiveQAPipeline
pipe = ExtractiveQAPipeline(reader, retriever)

נשאל שאלה, ונדפיס את 5 הציטוטים המתאימים ביותר כתשובה:

3_query.py

prediction = pipe.run(query="what is a transformer?", top_k_retriever=10, top_k_reader=5)

print(f"Question: {prediction['query']}")
for answer in prediction['answers']:
    print(f"Answer: {answer['answer']} (probability:{answer['probability']})")

 

התוצאות

שאלתי את המערכת את השאלות הבאות מתוך המאמר:

  • מהו טרנספורמר?
Question: What is a transformer?
Answer: a deep learning model (probability:0.6463984847068787)
Answer: GPT-2 (probability:0.9120923280715942)
Answer: an encoder-decoder architecture (probability:0.4997243583202362)
Answer: an attention mechanism without being an RNN (probability:0.21909530460834503)
Answer: semi-supervised learning (probability:0.4208458662033081)
  • במה הוא שונה מ-RNN?
Question: What makes it different from RNN?
Answer: Transformers do not require that the sequential data be processed in order (probability:0.5966780185699463)
Answer: attention mechanisms alone, without recurrent sequential processing (probability:0.29175233840942383)
Answer: Transformers do not rely on sequential processing (probability:0.21647100150585175)
Answer: contextual information (probability:0.09400469809770584)
Answer: an additional attention mechanism is inserted which instead draws relevant information from the encodings generated by the encoders (probability:0.11221755295991898)
  • מה זה Attention mechanism?
Question: What is the Attention mechanism?
Answer: let a model directly look at, and draw from, the state at any preceding point along the sentence (probability:0.8120094537734985)
Answer: weighing the influence of different parts of the input data (probability:0.9514950513839722)
Answer: takes in a set of input encodings from the previous encoder and weighs their relevance to each other to generate a set of output encodings (probability:0.31864359974861145)
Answer: draws relevant information from the encodings generated by the encoders (probability:0.27160751819610596)
Answer: Multi-head (probability:0.6701899766921997)
  • מה הארכיטקטורה עליה מבוסס הטרנספורמר?
Question: What architecture is it based on?
Answer: encoder-decoder (probability:0.8662871718406677)
Answer: GPT-2 (probability:0.5948620438575745)
Answer: Transformer (probability:0.9114766120910645)
Answer: Transformer (probability:0.4777696132659912)
Answer: transformer (probability:0.7358217239379883)

התוצאות די מדויקות ומפתיעות לטובה בהתחשב בעובדה שאנחנו משתמשים במודלים שפייסבוק וגוגל אימנו ללא קשר לצורך שלי לשאול שאלות על מאמר מסוים בויקיפדיה. התוצאות טובות גם ביחס לניסיונות שעשיתי להשתמש ב-BERT בפייתון או בגרסת ה-TensorflowJS. נראה שהשילוב של שלושה מודלים מסוג טרנספורמר עם מנוע החיפוש Elasticsearch מספק את הסחורה. כמו כן, משך הזמן שלוקח למערכת לענות על שאלות הוא כדקה על המחשב האישי שלי שמצויד במעבד מסוג CPU. יהיה מעניין לבחון את השפעת השימוש ב-GPU על משך החיפוש בפרט אם נרצה להעזר במערכת בתוך אפליקציות לשימוש יחידים וחברות.

במדריך הבא בסדרה נהפוך את מנגנון השו"ת לאפליקציה אינטרנטית.

 

גם זה יעניין אותך

הטרנספורמרים משנים את עולם הבינה המלאכותית

סיכום מאמר ויקיפדיה באמצעות המודל המתקדם בעולם T5 של גוגל

הקמת אפליקציה אינטרנטית למערכת שאלות ותשובות מבוססת בינה מלאכותית

לכל המדריכים בנושא של למידת מכונה

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

הוסף תגובה חדשה

 

= 3 + 4