Chapter 120
evaluate text
NotebookPython 3 (ipykernel)25 cells
In [48]python · cell 1
python
import json
with open('documents-with-ids.json', 'rt') as f_in:
documents = json.load(f_in)In [4]python · cell 2
python
from elasticsearch import Elasticsearch
es_client = Elasticsearch('http://localhost:9200')
index_settings = {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"text": {"type": "text"},
"section": {"type": "text"},
"question": {"type": "text"},
"course": {"type": "keyword"},
"id": {"type": "keyword"},
}
}
}
index_name = "course-questions"
es_client.indices.delete(index=index_name, ignore_unavailable=True)
es_client.indices.create(index=index_name, body=index_settings)Output
ObjectApiResponse({'acknowledged': True, 'shards_acknowledged': True, 'index': 'course-questions'})In [5]python · cell 3
python
from tqdm.auto import tqdm
for doc in tqdm(documents):
es_client.index(index=index_name, document=doc)Output
0%| | 0/948 [00:00<?, ?it/s]
In [46]python · cell 4
python
def elastic_search(query, course):
search_query = {
"size": 5,
"query": {
"bool": {
"must": {
"multi_match": {
"query": query,
"fields": ["question^3", "text", "section"],
"type": "best_fields"
}
},
"filter": {
"term": {
"course": course
}
}
}
}
}
response = es_client.search(index=index_name, body=search_query)
result_docs = []
for hit in response['hits']['hits']:
result_docs.append(hit['_source'])
return result_docsIn [8]python · cell 5
python
elastic_search(
query="I just discovered the course. Can I still join?",
course="data-engineering-zoomcamp"
)Output
[{'text': "Yes, even if you don't register, you're still eligible to submit the homeworks.\nBe aware, however, that there will be deadlines for turning in the final projects. So don't leave everything for the last minute.",
'section': 'General course-related questions',
'question': 'Course - Can I still join the course after the start date?',
'course': 'data-engineering-zoomcamp',
'id': '7842b56a'},
{'text': 'You can start by installing and setting up all the dependencies and requirements:\nGoogle cloud account\nGoogle Cloud SDK\nPython 3 (installed with Anaconda)\nTerraform\nGit\nLook over the prerequisites and syllabus to see if you are comfortable with these subjects.',
'section': 'General course-related questions',
'question': 'Course - What can I do before the course starts?',
'course': 'data-engineering-zoomcamp',
'id': '63394d91'},
{'text': 'Yes, we will keep all the materials after the course finishes, so you can follow the course at your own pace after it finishes.\nYou can also continue looking at the homeworks and continue preparing for the next cohort. I guess you can also start working on your final capstone project.',
'section': 'General course-related questions',
'question': 'Course - Can I follow the course after it finishes?',
'course': 'data-engineering-zoomcamp',
'id': 'a482086d'},
{'text': 'Yes, the slack channel remains open and you can ask questions there. But always sDocker containers exit code w search the channel first and second, check the FAQ (this document), most likely all your questions are already answered here.\nYou can also tag the bot @ZoomcampQABot to help you conduct the search, but don’t rely on its answers 100%, it is pretty good though.',
'section': 'General course-related questions',
'question': 'Course - Can I get support if I take the course in the self-paced mode?',
'course': 'data-engineering-zoomcamp',
'id': 'eb56ae98'},
{'text': "You don't need it. You're accepted. You can also just start learning and submitting homework without registering. It is not checked against any registered list. Registration is just to gauge interest before the start date.",
'section': 'General course-related questions',
'question': 'Course - I have registered for the Data Engineering Bootcamp. When can I expect to receive the confirmation email?',
'course': 'data-engineering-zoomcamp',
'id': '0bbf41ec'}]In [9]python · cell 6
python
import pandas as pdIn [10]python · cell 7
python
df_ground_truth = pd.read_csv('ground-truth-data.csv')In [13]python · cell 8
python
ground_truth = df_ground_truth.to_dict(orient='records')In [19]python · cell 9
python
relevance_total = []
for q in tqdm(ground_truth):
doc_id = q['document']
results = elastic_search(query=q['question'], course=q['course'])
relevance = [d['id'] == doc_id for d in results]
relevance_total.append(relevance)Output
0%| | 0/4627 [00:00<?, ?it/s]
In [21]python · cell 10
python
example = [
[True, False, False, False, False], # 1,
[False, False, False, False, False], # 0
[False, False, False, False, False], # 0
[False, False, False, False, False], # 0
[False, False, False, False, False], # 0
[True, False, False, False, False], # 1
[True, False, False, False, False], # 1
[True, False, False, False, False], # 1
[True, False, False, False, False], # 1
[True, False, False, False, False], # 1
[False, False, True, False, False], # 1/3
[False, False, False, False, False], # 0
]
# 1 => 1
# 2 => 1 / 2 = 0.5
# 3 => 1 / 3 = 0.3333
# 4 => 0.25
# 5 => 0.2
# rank => 1 / rank
# none => 0In [24]python · cell 11
python
def hit_rate(relevance_total):
cnt = 0
for line in relevance_total:
if True in line:
cnt = cnt + 1
return cnt / len(relevance_total)In [29]python · cell 12
python
def mrr(relevance_total):
total_score = 0.0
for line in relevance_total:
for rank in range(len(line)):
if line[rank] == True:
total_score = total_score + 1 / (rank + 1)
return total_score / len(relevance_total)In [30]python · cell 13
python
hit_rate(example)Output
0.5833333333333334
In [31]python · cell 14
python
mrr(example)Output
0.5277777777777778
- hit-rate (recall)
- Mean Reciprocal Rank (mrr)
In [32]python · cell 16
python
hit_rate(relevance_total), mrr(relevance_total)Output
(0.7395720769397017, 0.6032418413658963)
In [34]python · cell 17
python
import minsearch
index = minsearch.Index(
text_fields=["question", "text", "section"],
keyword_fields=["course", "id"]
)
index.fit(documents)Output
<minsearch.Index at 0x29109dae150>
In [38]python · cell 18
python
def minsearch_search(query, course):
boost = {'question': 3.0, 'section': 0.5}
results = index.search(
query=query,
filter_dict={'course': course},
boost_dict=boost,
num_results=5
)
return resultsIn [39]python · cell 19
python
relevance_total = []
for q in tqdm(ground_truth):
doc_id = q['document']
results = minsearch_search(query=q['question'], course=q['course'])
relevance = [d['id'] == doc_id for d in results]
relevance_total.append(relevance)Output
0%| | 0/4627 [00:00<?, ?it/s]
In [40]python · cell 20
python
hit_rate(relevance_total), mrr(relevance_total)Output
(0.7722066133563864, 0.661454506159499)
Compare with ES results:
code
(0.7395720769397017, 0.6032418413658963)In [42]python · cell 22
python
def evaluate(ground_truth, search_function):
relevance_total = []
for q in tqdm(ground_truth):
doc_id = q['document']
results = search_function(q)
relevance = [d['id'] == doc_id for d in results]
relevance_total.append(relevance)
return {
'hit_rate': hit_rate(relevance_total),
'mrr': mrr(relevance_total),
}In [44]python · cell 23
python
evaluate(ground_truth, lambda q: elastic_search(q['question'], q['course']))Output
0%| | 0/4627 [00:00<?, ?it/s]
{'hit_rate': 0.7395720769397017, 'mrr': 0.6032418413658963}In [45]python · cell 24
python
evaluate(ground_truth, lambda q: minsearch_search(q['question'], q['course']))Output
0%| | 0/4627 [00:00<?, ?it/s]
{'hit_rate': 0.7722066133563864, 'mrr': 0.661454506159499}In [ ]python · cell 25
python
