diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/lab-extractive-question-answering.ipynb b/lab-extractive-question-answering.ipynb index 0cf5b39..3411eb8 100644 --- a/lab-extractive-question-answering.ipynb +++ b/lab-extractive-question-answering.ipynb @@ -112,7 +112,7 @@ }, "outputs": [], "source": [ - "!pip install -qU datasets pinecone-client sentence-transformers torch" + "# !pip install -qU datasets pinecone-client sentence-transformers torch" ] }, { @@ -242,7 +242,8 @@ "from datasets import load_dataset\n", "\n", "# load the squad dataset into a pandas dataframe\n", - "df = load_dataset(\"squad\", split=\"train\").to_pandas()" + "df = load_dataset(\"squad\", split=\"train\").to_pandas()\n", + "df.head()" ] }, { @@ -260,9 +261,9 @@ "outputs": [], "source": [ "# select only title and context column\n", - "df = None\n", + "df = df[[\"title\", \"context\"]]\n", "# drop rows containing duplicate context passages\n", - "df = None\n", + "df = df.drop_duplicates(subset=[\"context\"])\n", "df" ] }, @@ -293,7 +294,7 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install -qU langchain-pinecone pinecone-notebooks" + "# !pip install -qU langchain-pinecone pinecone-notebooks" ] }, { @@ -310,11 +311,9 @@ "spec = ServerlessSpec(\n", " cloud=\"aws\", region=\"us-east-1\"\n", ")\n", - "\n", "# connect to pinecone environment\n", "pc = Pinecone(\n", - " api_key = PINECONE_API_KEY,\n", - " environment='us-east-1' # find next to API key in console\n", + " api_key = PINECONE_API_KEY\n", ")" ] }, @@ -337,14 +336,18 @@ }, "outputs": [], "source": [ - "index_name = None\n", + "index_name = \"question-answering\"\n", "\n", "# check if the extractive-question-answering index exists\n", - "if index_name not in pinecone.list_indexes().names():\n", - " # create the index if it does not exist\n", - " None\n", + "if not pc.has_index(index_name):\n", + " pc.create_index(\n", + " name=index_name,\n", + " dimension=384,\n", + " metric=\"cosine\",\n", + " spec=spec\n", + " )\n", "# connect to extractive-question-answering index we created\n", - "index = pinecone.Index(index_name)" + "index = pc.Index(index_name)" ] }, { @@ -550,7 +553,7 @@ "# set device to GPU if available\n", "device = 'cuda' if torch.cuda.is_available() else 'cpu'\n", "# load the retriever model from huggingface model hub\n", - "retriever = None #use the 'multi-qa-MiniLM-L6-cos-v1' model from HuggingFace to build the retriever\n", + "retriever = SentenceTransformer('sentence-transformers/multi-qa-MiniLM-L6-cos-v1')\n", "retriever" ] }, @@ -609,17 +612,17 @@ "\n", "for i in tqdm(range(0, len(df), batch_size)):\n", " # find end of batch\n", - " None\n", + " i_end = min(i + batch_size, len(df))\n", " # extract batch\n", - " None\n", + " batch = df.iloc[i:i_end]\n", " # generate embeddings for batch\n", - " emb = None\n", + " emb = retriever.encode(batch[\"context\"].tolist()).tolist()\n", " # get metadata\n", - " meta = None\n", + " meta = batch.to_dict(orient=\"records\")\n", " # create unique IDs\n", - " ids = None\n", + " ids = [str(idx) for idx in batch.index]\n", " # add all to upsert list\n", - " to_upsert = None\n", + " to_upsert = list(zip(ids, emb, meta))\n", " # upsert/insert these records to pinecone\n", " _ = index.upsert(vectors=to_upsert)\n", "\n", @@ -718,12 +721,24 @@ }, "outputs": [], "source": [ - "from transformers import pipeline\n", + "from transformers import AutoTokenizer, AutoModelForQuestionAnswering\n", "\n", "model_name = 'deepset/electra-base-squad2'\n", - "# load the reader model into a question-answering pipeline\n", - "reader = pipeline(tokenizer=model_name, model=model_name, task='question-answering', device=device)\n", - "reader" + "\n", + "tokenizer = AutoTokenizer.from_pretrained(model_name)\n", + "model = AutoModelForQuestionAnswering.from_pretrained(model_name).to(device)\n", + "def reader(question, context):\n", + " inputs = tokenizer(question, context, return_tensors='pt', truncation=True, max_length=512).to(device)\n", + " with torch.no_grad():\n", + " outputs = model(**inputs)\n", + " \n", + " start = outputs.start_logits.argmax()\n", + " end = outputs.end_logits.argmax() + 1\n", + " \n", + " answer = tokenizer.convert_tokens_to_string(\n", + " tokenizer.convert_ids_to_tokens(inputs['input_ids'][0][start:end])\n", + " )\n", + " return {\"answer\": answer, \"score\": outputs.start_logits.max().item()}" ] }, { @@ -748,11 +763,11 @@ "# gets context passages from the pinecone index\n", "def get_context(question, top_k):\n", " # generate embeddings for the question\n", - " xq = None\n", + " xq = retriever.encode(question)\n", " # search pinecone index for context passage with the answer\n", - " xc = None\n", + " xc = index.query(vector=xq.tolist(), top_k=5, include_metadata=True)\n", " # extract the context passage from pinecone search result\n", - " c = None\n", + " c = [x['metadata']['context'] for x in xc['matches']]\n", " return c" ] }, @@ -942,7 +957,7 @@ }, "gpuClass": "standard", "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": ".venv (3.14.0)", "language": "python", "name": "python3" }, @@ -956,7 +971,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.8" + "version": "3.14.0" }, "papermill": { "default_parameters": {}, @@ -970,11 +985,6 @@ "start_time": "2021-04-15T21:06:38.122812", "version": "2.3.3" }, - "vscode": { - "interpreter": { - "hash": "5fe10bf018ef3e697f9035d60bf60847932a12bface18908407fd371fe880db9" - } - }, "widgets": { "application/vnd.jupyter.widget-state+json": { "00b344135da443ac90e6e6a0f53cfe0f": {