openai/openai-python

Public

mirrored from https://github.com/openai/openai-pythonAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.20.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

examples/semanticsearch/semanticsearch.py

126lines · modeblame

3c6d4cd6Greg Brockman5 years ago1#!/usr/bin/env python
2import argparse
3import logging
4import sys
5from typing import List
6
62f8d40fMadeleine Thompson4 years ago7import openai
8
3c6d4cd6Greg Brockman5 years ago9logger = logging.getLogger()
10formatter = logging.Formatter("[%(asctime)s] [%(process)d] %(message)s")
11handler = logging.StreamHandler(sys.stderr)
12handler.setFormatter(formatter)
13logger.addHandler(handler)
14
15DEFAULT_COND_LOGP_TEMPLATE = (
16"<|endoftext|>{document}\n\n---\n\nThe above passage is related to: {query}"
17)
18SCORE_MULTIPLIER = 100.0
19
20
21class SearchScorer:
22def __init__(
23self, *, document, query, cond_logp_template=DEFAULT_COND_LOGP_TEMPLATE
24):
25self.document = document
26self.query = query
27self.cond_logp_template = cond_logp_template
28self.context = self.cond_logp_template.format(
29document=self.document, query=self.query
30)
31
32def get_context(self):
33return self.context
34
35def get_score(self, choice) -> float:
36assert choice.text == self.context
37logprobs: List[float] = choice.logprobs.token_logprobs
38text = choice.logprobs.tokens
39text_len = sum(len(token) for token in text)
40if text_len != len(self.context):
41raise RuntimeError(
42f"text_len={text_len}, len(self.context)={len(self.context)}"
43)
44total_len = 0
45last_used = len(text)
46while total_len < len(self.query):
47assert last_used > 0
48total_len += len(text[last_used - 1])
49last_used -= 1
50max_len = len(self.context) - self.cond_logp_template.index("{document}")
51assert total_len + len(self.document) <= max_len
52logits: List[float] = logprobs[last_used:]
53return sum(logits) / len(logits) * SCORE_MULTIPLIER
54
55
56def semantic_search(engine, query, documents):
57# add empty document as baseline
58scorers = [
59SearchScorer(document=document, query=query) for document in [""] + documents
60]
61completion = openai.Completion.create(
62engine=engine,
63prompt=[scorer.get_context() for scorer in scorers],
64max_tokens=0,
65logprobs=0,
66echo=True,
67)
68# put the documents back in order so we can easily normalize by the empty document 0
69data = sorted(completion.choices, key=lambda choice: choice.index)
70assert len(scorers) == len(
71data
72), f"len(scorers)={len(scorers)} len(data)={len(data)}"
73scores = [scorer.get_score(choice) for scorer, choice in zip(scorers, data)]
74# subtract score for empty document
75scores = [score - scores[0] for score in scores][1:]
76data = {
77"object": "list",
78"data": [
79{
80"object": "search_result",
81"document": document_idx,
82"score": round(score, 3),
83}
84for document_idx, score in enumerate(scores)
85],
86"model": completion.model,
87}
88return data
89
90
91def main():
92parser = argparse.ArgumentParser(description=None)
93parser.add_argument(
94"-v",
95"--verbose",
96action="count",
97dest="verbosity",
98default=0,
99help="Set verbosity.",
100)
101parser.add_argument("-e", "--engine", default="ada")
102parser.add_argument("-q", "--query", required=True)
103parser.add_argument("-d", "--document", action="append", required=True)
104parser.add_argument("-s", "--server-side", action="store_true")
105args = parser.parse_args()
106
107if args.verbosity == 1:
108logger.setLevel(logging.INFO)
109elif args.verbosity >= 2:
110logger.setLevel(logging.DEBUG)
111
112if args.server_side:
113resp = openai.Engine(id=args.engine).search(
114query=args.query, documents=args.document
115)
116resp = resp.to_dict_recursive()
117print(f"[server-side semantic search] {resp}")
118else:
119resp = semantic_search(args.engine, query=args.query, documents=args.document)
120print(f"[client-side semantic search] {resp}")
121
122return 0
123
124
125if __name__ == "__main__":
126sys.exit(main())