היתכנות הפרוייקט
הטרנספורמרים משנים את עולם הבינה המלאכותית
מחבר: יוסי בן הרוש בתאריך: 19.03.2021מודלים מסוג טרנספורמרים משנים את עולם הבינה המלאכותית ומגיעים להישגים על אנושיים במשימות של עיבוד שפה טבעית (NLP).
קיימים כמה עשרות טרנספורמרים, מתוכם שניים בולטים במיוחד: GPT של OpenAI שמייצר טקסט ברמה חסרת תקדים, עונה על שאלות ומסכם טקסטים ו-BERT של גוגל שמשמש, בין היתר, לקידוד טקסט ואנליזת סנטימנט.
ספריית transformers מאפשרת לעבוד עם הטרנספורמרים השונים בממשק אחיד שנועד לשרת את חובבי Tensorflow כמו גם את מקצועני PyTorch. הספרייה מציעה שפע של אפשרויות, ומצאתי שהדרך הטובה ביותר להתחיל את ההיכרות עם הספרייה היא מדף תיאורי המקרה בכתובת: https://huggingface.co/transformers/usage.html . בין האפשרויות שהדף מדגים אפשר למצוא: סיווג טקסטים, תשובות לשאלות, מודלים של שפה, תרגום ויצירת טקסט.
במדריך זה אדגים סיכום ויצירה של טקסט.
נתקין את הספרייה:

סיכום טקסט
הדרך הפשוטה ביותר לסכם טקסט היא לעבוד עם פונקצית pipeline שנעביר לה את הפרמטר "summarization".
from transformers import pipeline summarizer = pipeline("summarization")
הפונקציה משתמשת בטרנספומר Bart שהוא פיתוח של Bert.
נעביר לפונקציה את 4 הפסקאות הראשונות של מאמר בויקיפדיה על "רפסודיה בוהמית" של להקת קווין:
text_list = ["Bohemian Rhapsody is a song by the British rock band Queen. It was written by Freddie Mercury for the band's 1975 album A Night at the Opera. The song is a six-minute suite, notable for its lack of a refraining chorus and consisting of several sections: an intro, a ballad segment, an operatic passage, a hard rock part and a reflective coda. Bohemian Rhapsody is one of the few songs to emerge from the 1970s progressive rock movement to achieve widespread commercial success and appeal to a mainstream audience."] ARTICLE = ' '.join(text_list)
ונדפיס את תוצאה:
print(summarizer(ARTICLE, max_length=130, min_length=30))
Bohemian Rhapsody was written by Freddie Mercury for the band's 1975 album A Night at the Opera . The song is a six-minute suite, notable for its lack of a refraining chorus and consisting of several sections . It is one of the few songs to emerge from the 1970s progressive rock movement to achieve widespread commercial success and appeal to a mainstream audience.
אנחנו לא חייבים להשתמש ב-Bart. נשתמש במודל T5 הנחשב לטוב ביותר לסיכום טקסט.
את המודלים השונים שמציעה הספרייה ניתן למצוא בדף https://huggingface.co/models
כדי להשתמש במודל אחר נגדיר מודל וטוקנייזר:
from transformers import AutoModelWithLMHead, AutoTokenizer
model = AutoModelWithLMHead.from_pretrained("t5-base") tokenizer = AutoTokenizer.from_pretrained("t5-base")
נעשה טוקניזציה:
inputs = tokenizer.encode("summarize: " + ARTICLE, return_tensors="tf", max_length=512)
  • המודל מוגבל ל- 512 טוקנים לכל היותר.
  • אנחנו מצפים לטוקנים בתצוגת טנסור של Tensorflow (ולא PyTorch).
נריץ את המודל:
outputs = model.generate(inputs, max_length=150, min_length=40, length_penalty=2.0, num_beams=4, early_stopping=True)
  • הגדרנו את אורך הסיכום.
  • יתר הפרמטרים מגבילים את אפשרויות החיפוש.
נמיר את תוצא המודל מטוקנים למילים:
summary = tokenizer.decode(outputs[0], skip_special_tokens=True)
נדפיס את התוצאה:
print(summary)
bohemian Rhapsody is a song by the British rock band queen. it was written by Freddie Mercury for the band's 1975 album a night at the opera. the song is considered one of the greatest rock songs of all time.
קצר ולעניין.
 
יצירת טקסט
ביצירת טקסט אנחנו מצפים מהמחשב להשלים את הטקסט שאנחנו מספקים לו. המודל הטוב בתחום הוא GPT-2 שבו משתמשת הספרייה עבור פונקצית ה-pipeline.
כל מה שצריך הוא 3 שורות של קוד, ולהעביר לפונקציה את הפרמטר "text-generation":
passage = "Bohemian Rhapsody is a song by the British rock band Queen. It was written by Freddie Mercury for the band's 1975 album A Night at the Opera. The song is a six-minute suite, notable for its lack of a refraining chorus and consisting of several sections: an intro, a ballad segment, an operatic passage, a hard rock part and a reflective coda. Bohemian Rhapsody is one of the few songs to emerge from the 1970s progressive rock movement to achieve widespread commercial success and appeal to a mainstream audience."
text_generator = pipeline("text-generation")
print(text_generator(passage, max_length=200))
התוצאה:
Pianist and composer Steve Stokoe is known for his pioneering work with piano players, particularly the piano virtuoso Michael Lonergan, who played the bass with pianist James Blake in 1972, and his solo work with electric piano player Tom Watson in 1979.\n\nThe work of C. W. White was published as a pamphlet in 1982. White's work on C. W. White
אפשר גם להשתמש במודל אחר או לשנות את הפרמטרים כדי לקבל תוצאות שונות.
ניצור מודל וטוקנייזר:
from transformers import TFAutoModelWithLMHead, AutoTokenizer
model = TFAutoModelWithLMHead.from_pretrained("gpt2") tokenizer = AutoTokenizer.from_pretrained("gpt2")
  • אני משתמש ב-GPT-2 שמייצר כותרות בעיתונות העולם אבל אפשר לבחור מודל אחר שמתמחה ביצירת טקסטים דוגמת: XLNet.
נהפוך את הפסקה לטוקנים:
inputs = tokenizer.encode(passage, add_special_tokens=False, return_tensors="tf")
נריץ את המודל:
outputs = model.generate(inputs, max_length=250, do_sample=True, temperature=0.7, top_p=0.8, top_k=50)
הפרמטרים:
  • do_sample - כדי לנסות למנוע מהמודל לחזור על עצמו.
  • temperature - ערכים נמוכים מ-1 בשביל תוצאות שמרניות.
  • הפרמטרים הנוספים מגבילים את מספר האפשרויות וסבירותם.
נמיר את הטוקנים שמייצר המודל למילים:
generated = tokenizer.decode(outputs[0])
והתוצאה:
print(generated)
The title song is the opening track from Queen's 1973 album The Road to Hell. The song is a five-minute suite with two parts that are a breakaway from the rest of the song. The song is sung by a young woman named Margaret, who is a member of the band and a member of Queen's 1977 album The Road to Hell. The song is written by the band's guitarist, Alan Bennett. The song is recorded by the band's producer, David J. Taylor. The song is the first and only song recorded for Queen in the band's lifetime.
The title track is the second and only song recorded for Queen in the band's lifetime
כותבת מחדש את ההיסטוריה של עולם המוזיקה.
אם אתה רוצה לסכם טקסטים ארוכים אז כדאי שתקרא את המדריך סיכום מאמר ויקיפדיה באמצעות המודל המתקדם בעולם T5 של גוגל.
 
מערכת שאלות ותשובות מבוססת בינה מלאכותית​​​​
מחבר: יוסי בן הרוש בתאריך: 17.04.2021במדריך נשתמש במנוע החיפוש Elasticsearch ובבינה מלאכותית מבוססת טרנספורמר כדי לענות על שאלות אודות מאמר. Elasticsearch הוא מנוע חיפוש המתמחה באיתור טקסט ואף ניתן לשלבו עם מודלים מסוג טרנספורמרים - המובילים בעולם בתחום עיבוד שפה טבעית NLP.

את המאמר שנוריד מויקיפדיה נקודד באמצעות מודל מאומן מסוג טרנספורמר לוקטורים שכל אחד מהם מייצג פסקה. את הוקטורים נאחסן במסד נתונים 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 של גוגל
הקמת אפליקציה אינטרנטית למערכת שאלות ותשובות מבוססת בינה מלאכותית
לכל המדריכים בנושא של למידת מכונה