From f0676358d03303b16bd5578c6b2a42fdb31b3326 Mon Sep 17 00:00:00 2001 From: Selfscrum Date: Sun, 7 May 2023 23:58:18 +0200 Subject: [PATCH] langchain-image --- images/langchain.png | Bin 0 -> 79237 bytes learning.ipynb | 1146 +++++++++++++++++++++--------------------- 2 files changed, 573 insertions(+), 573 deletions(-) create mode 100644 images/langchain.png diff --git a/images/langchain.png b/images/langchain.png new file mode 100644 index 0000000000000000000000000000000000000000..be0b72557273712b619cce47f2dace8ba289de3c GIT binary patch literal 79237 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�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 Bs)7Ij literal 0 HcmV?d00001 diff --git a/learning.ipynb b/learning.ipynb index f4c35bb..dd8636e 100644 --- a/learning.ipynb +++ b/learning.ipynb @@ -1,574 +1,574 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "view-in-github", - "colab_type": "text" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "VjbM5InfQjTM" - }, - "source": [ - "# Setting up a Hosting Provider\n", - "\n", - "If you have worked with Cloud providers, you know the setup of virtual machines. While for classic projects, a virtual machine is rather a convenience (quick setup, automated installation, secured environment etc.), it becomes a \"must\" for experiments with AI. Models, data, and processing needs grow so quick that maintaining your own setup is not feasible any longer - at least until you leave learning mode and decide to build your own AI factory 😅.\n", - "\n", - "For first steps, [Google Colab](https://colab.research.google.com/) is a good and easy to use option:\n", - "* Free for initial steps.\n", - "* Integrated with your Google Account and Drive, so you have a project store already.\n", - "* Integrated with Github, so you can easily maintain the development of your notebooks in an orderly fashion.\n", - "* Paid upgrades possible if you need more.\n", - "* Local runtime of Jupyter possible if you already have resources available ([Setup Info ](https://research.google.com/colaboratory/local-runtimes.html), not yet tried).\n", - "\n", - "Especially in the free environment, Colab has some performance and quality issues. You will learn pretty quick about broken tasks because your (virtual) hardware is not sufficient. So if you want to upgrade to Colab Pro or move to a more specialized provider, feel free to do so. I tried some few, but are not yet able to compare them systematically since I basically used only beginner's feature sets.\n", - "\n", - "* [RunPod](https://www.runpod.io/) - GPU Cloud with pre-setup environments\n", - "* [Paperspace](https://paperspace.com) - Notebooks, GPU, Deployment Services\n", - "* [HuggingFace](https://huggingface.co/) - Home of the Open Source Models, Development and Deployment ecosystem (you will need here an account in any case if you want to use one of the open models like Stable Diffusion or GPT-J).\n", - "\n", - "---\n", - "> **Keep an eye on where you commit to usage fees, there are many tools out there that require a funding source. Even if it is only some cents for initial experiments, consumption can get out of hand easily.**\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "XLk0C-xbWdn_" - }, - "source": [ - "# Know Your Tools\n", - "\n", - "It's great that ChatGPT can write now software code for you, but what do you do with it? Today's tool chain maturity still requires that you integrate partial results into an end-to-end workstream. So you should at least roughly know what you are doing with the individual components.\n", - "\n", - "For developers this might be familiar turf to some extent, so you can quickly flip through this block. For everyone else, this chapter is not necessarily a roadblock before you can jump into AI joys, but it can't hurt to make yourself familiar with these concepts before or in parallel to the AI learning, depending how you learn best.\n", - "\n", - "## Git\n", - "git is the central repository for developer articats and platforms like Github have evolved to a complete tech ecosystem. I don't want to write just another git manual, there is plenty out there. Checkout some places to learn about the basic lifecycle, this is enough for our purposes.\n", - "\n", - "Some interesting learning pages are\n", - "* [Roger Dudler's git guide](https://rogerdudler.github.io/git-guide/index.html)\n", - "* [Intro videos for beginners](https://www.git-tower.com/learn/git/videos/), use the CLI version of the videos.\n", - "\n", - "## Jupyter Notebook\n", - "You're here, aren't you? So you already managed to see a notebook, and when we come to the action part, you will get interactive too. Let's stick to the headline \"documentation and coding environment\" as the mental model why we are here.\n", - "\n", - "No worries, we don't code yet. If you are on a Colab environment, let's just use a little predefined notebook integration to insert a codeblock semi-automatically. Click the folder on the left toolbar, then select the folder with the google drive symbol. **Do not** accept the access options. \n", - "\n", - "![gdrive-image](https://github.com/selfscrum/hello-ai/blob/main/images/gdrive.png?raw=1)\n", - "\n", - "This will cause Colab to add a code block below the currently selected one.\n", - "\n", - "![gdrive-block-image](https://github.com/selfscrum/hello-ai/blob/main/images/gdrive-block.png?raw=1)\n", - "\n", - "If you hover over the area between the square brackets, a play symbol appears. Click on it, and confirm all the warnings and acceptance screens. After some seconds, the code block (this is what it actually is) is executed successfully, indicated by a green checkmark. You know now that this block has run in this session. \n", - "\n", - "![gdrive-success-image](https://github.com/selfscrum/hello-ai/blob/main/images/gdrive-success.png?raw=1)\n", - "\n", - "If you refresh now the folder view in the left panel, you see the mounted drive folder which points to your private Google Drive space. This means you can access all files in Google Drive from within the Notebook. You might want to use it for session results, and data and model uploads. All assets that are meant to persist the session, should be stored directly in the notebook or as files in Github." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Gkg475CCwYbq" - }, - "source": [ - "\n", - "## Python\n", - "\n", - "I am not a professional developer and know Python only from occasional small coding sessions. What I see in most notebooks is some glue code or a sequence of setup steps for the technology we are going to use. So, if you have a rough understanding what coding with Python is, this won't hurt. If all goes well, you do not need to jump into the code but just have to start it - this is the beauty of Jupyter Notebooks, which can guide you through an interactive session without jumping in every tarpit.\n", - "\n", - "But if something goes wrong, you might get some error messages. It is surely beneficial if you can roughly interpret them, or check answers from Google to fix the problem.\n", - "\n", - "## Markdown\n", - "\n", - "You should be familiar with the markdown notation since this is how Jupyter Notebook texts are written. You find this supported in many tools like Github, Notion, too name some. It is easy and compelling and relieves you from the \"Learn MS Word\" burden. See [Markdown Guide](https://www.markdownguide.org/)\n", - "\n", - "## Image manipulation\n", - "\n", - "If you are interested in image processing, you might need some processing tools since current AI has quite some prerequisites about image formats etc. You can use any desktop app of your choice, or you can use web services like [Birme](https://birme.net) to support your batch workflow.\n", - "\n", - "An increasingly interesting combination is the creation of quick low-scale images by the generation model and an AI-based scale-up app that creates a high-res, high-quality image afterwards.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "J8iqqCuWH3d9" - }, - "source": [ - "# Find your Heros\n", - "\n", - "Learning alone is not how you get up to speed. Find a learning group to discuss your targets, your plans, your issues, and to celebrate your successes. This could be someone from your surroundings, or maybe you find a discord forum around one of the tools we are going to see.\n", - "\n", - "At least you should tune into some youtube videos to get some basic conceptual understanding. As always for a new topic, it's hard to know what you are looking for, so you have to try out some channels to find the proper ones that fit your needs and support you best.\n", - "\n", - "I learned from (in order of appearance on my radar)\n", - "* [Kris Kashtanova](https://www.kris.art/), an early adoptor of image creation on Midjourney.\n", - "* [Sebastian Kamph](https://www.youtube.com/@sebastiankamph) about Stable Diffusion, deforum and Automatic1111.\n", - "* [Amelia Player aka PromptMuse](https://www.youtube.com/@promptmuse) about various image and video processing workflows.\n", - "* [David Shapiro](https://www.youtube.com/@DavidShapiroAutomator) GPT3 and ChatGPT experiments with Python. He is always \"5 minutes ahead\" of you, so if you like to follow searchers, this is the one for you.\n", - "* [Olivio Sarikas](https://www.youtube.com/@OlivioSarikas) for prompt engineering details for imaging.\n", - "* [Maggie Appleton](https://maggieappleton.com/forest-talk), Designer and anthropologist with some impressive and well-researched essays about the topic." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gTj9i_BzjvmN" - }, - "source": [ - "# Start experimenting\n", - "\n", - "What are your most urgent questions? how do you enter the scene? This is hard to answer generally since everyone comes from a different path and has different knowledge.\n", - "\n", - "If you are completely new to AI and coding, you might want to start easy with\n", - "* [ChatGPT](https://chat.openai.com/). Very low entry barrier, you get a chat window and can start talking to the ai without any background knowledge.\n", - "* [Midjourney](https://midjourney.com/). Image generation site. To use it, you need to have a Discord account, which is for people new to the scene something like \"Teams on steroids\". It has its roots in the gamer scene but is meanwhile a popular community platform for many new initiatives. Follow the guidelines there to sign up, then \"Join the Beta\" on Midjourney." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "82Ix-cma5pld" - }, - "source": [ - "# Advanced Experiments\n", - "\n", - "> The current cambrian explosion of advances in the AI ecosystem is breathtaking. New tools are released by the hour, and many libraries I use here are under steady development. Some breaks might occur. I try to keep everything somewhat up to date. If you come across an error please let me know by creating an issue [here](https://github.com/selfscrum/hello-ai/issues)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1n6SheTlRmI9" - }, - "source": [ - "## Build a Knowledge Hub Around a GPT Model\n", - "As impressive GPT is, it has one big shortcoming:\n", - "\n", - "---\n", - "> **You can't infuse new knowledge easily**\n", - "---\n", - "\n", - "This might be surprising, isn't this what a Large Language Model (LLM) is all about? Yes and no. Training of an LLM is a long-during expensive task which requires vast amounts of texts. A typical GPT user cannot afford these resources, so that is a limiting factor. In addition, training takes a lot of time, so scenarios where your knowledge is varying more often (e.g. look at a typical IT curriculum, or even worse, at a marketing customer pipeline), you are at a loss with that approach.\n", - "\n", - "Another strategy is to built semantic indices of your current knowledge sets (also known as **vectors**, since each **chunk** (piece of knowledge) can be mapped to a mathematical multidemensional vector representing the information in the knowledge piece. If you then ask something, this question is vectorized as well and the vector store can easily find related answers by comparing the vectors. These answers can then be passed into GPT to phrase a good response to your original request.\n", - "\n", - "![vectors](https://github.com/selfscrum/hello-ai/blob/main/images/vectors.png?raw=1)\n", - "\n", - "That's what we are going to build here. A new open source project named LlamaIndex is supporting just that, and, even better, has already a lot of connectors to typical knowledge stores, like databases, Slack, Notion etc. Let's see how this works out!" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bYR9IKCLMoVh" - }, - "source": [ - "### Install LlamaIndex and do a Quick Starter Tour\n", - "\n", - "We can follow the tutorial from [here](https://gpt-index.readthedocs.io/en/latest/getting_started/installation.html):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "qr_pcGENUwQn" - }, - "outputs": [], - "source": [ - "!pip install --upgrade pip\n", - "!pip install llama-index" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "XMWZkGn-g_E0" - }, - "source": [ - "I store my openapi key on the private part of My Google Drive. This removes the need to edit a public notebook every time. The file must contain only the key (no newline!)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "gomXEMJCVWYP" - }, - "outputs": [], - "source": [ - "def open_file(filepath):\n", - " with open(filepath, 'r', encoding='utf-8') as infile:\n", - " return infile.read()\n", - "\n", - "api_key = open_file('/content/drive/MyDrive/llama-openapikey.txt')\n", - "%set_env OPENAI_API_KEY=$api_key" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "JXbg-xPMW_1m" - }, - "source": [ - "Now you can install the current release of LlamaIndex (they just recently renamed, still sometimes referred to as `gpt_index`)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "REuJM75AXNi3" - }, - "outputs": [], - "source": [ - "!git clone https://github.com/jerryjliu/gpt_index.git" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "oLu3_8J5b4wu" - }, - "source": [ - "Start the code of an example to check whether the service is running." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "nEfG1DvSY0Dk" - }, - "outputs": [], - "source": [ - "from llama_index import GPTVectorStoreIndex, SimpleDirectoryReader\n", - "\n", - "%cd /content/gpt_index/examples/paul_graham_essay/\n", - "\n", - "documents = SimpleDirectoryReader('data').load_data()\n", - "index = GPTVectorStoreIndex.from_documents(documents)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "XuX3r3pRfnyb" - }, - "source": [ - "Now you can query the essay." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "bV6pbFP0co4Z" - }, - "outputs": [], - "source": [ - "query_engine = index.as_query_engine()\n", - "response = query_engine.query(\"What did the author do growing up?\")\n", - "print(response)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vXPPFDH15_BX" - }, - "source": [ - "### Process a book to get an online query engine\n", - "\n", - "In the example above we just read some 300 statements from that essay, and were able to give some satisfying answers.\n", - "\n", - "Let's now increase the stakes a little. I am going to import a PDF ebook (\"Practical Process Automation\" of Bernd Ruecker, about 300 pages, 18 MB) in a simple vector index - this is the general purpose index you can use for document retrieval, as we did before.\n", - "\n", - "You can do that to if you have the PDF, it was available free as a sponsored download for some time (but of course you can use any other text-based PDF as well. Make sure it's not too short.)\n", - "\n", - "I upload the document to my Google Drive and load it from there into the index.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "TpYCx-9O8kZc" - }, - "outputs": [], - "source": [ - "from pathlib import Path\n", - "from llama_index import download_loader\n", - "from llama_index import GPTVectorStoreIndex\n", - "\n", - "PDFReader = download_loader(\"PDFReader\")\n", - "\n", - "loader = PDFReader()\n", - "pdfdocuments = loader.load_data(file=Path('/content/drive/MyDrive/pdfs/PracticalProcessAutomation_Camunda.pdf'))\n", - "pdfindex = GPTVectorStoreIndex.from_documents(pdfdocuments)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "7ODaNXrN8_L3" - }, - "source": [ - "That was quite quick! Now we can query the book's knowledge." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "9mE97HN_8_mG" - }, - "outputs": [], - "source": [ - "query_engine = pdfindex.as_query_engine()\n", - "response = query_engine.query(\"Should we use Powerpoint for Process Modeling? Please name better alternatives.\")\n", - "print(response)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "hGGyMZwtMyp4" - }, - "source": [ - "### Process a second book \n", - "\n", - "Can we increase our knowledge base? For sure! We could just load another document into the index. I choose the \"Data Mesh\" book of Zhamak Dehghani (another sponsored copy)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "i32X06UHPDPI" - }, - "outputs": [], - "source": [ - "pdfdocs2 = loader.load_data(file=Path('/content/drive/MyDrive/pdfs/DataMesh.pdf'))\n", - "for doc in pdfdocs2:\n", - " pdfindex.insert(doc)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "G7vgR4V2PcC1" - }, - "source": [ - "This takes a little longer, the book has nearly 400 pages and is 55 MB in size. After a minute we are ready, and the same query as above might yield better results, especially when we ask now about topics that belong to both books, e.g. \"How can we use Practical Process Automation to implement Data Mesh?\" " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Vx39oARHNWtq" - }, - "source": [ - "This works all pretty well ✨, but we just scratched the surface. \n", - "\n", - "What we technically did now is loading both books (chopped into small chunks that the language model can process) into a list of vectors that we can search and pass on to a summarized answer. LlamaIndex does this for us in a nice and easy way, while hiding the complexities of the workflow. The structure is still simple though, and we have lots of option unrevealed.\n", - "\n", - "![llamaindexoptions](https://github.com/selfscrum/hello-ai/blob/main/images/llamaindex-options.png?raw=1)\n", - "\n", - "So, we must decide how we can improve our knowledge base next:\n", - "\n", - "* Increase Token Size, or make it variable, to allow longer answers,\n", - "* Allow other language models than the default, or provide parameter tuning tweaks,\n", - "* Use another index structure to support hierarchical content in a better way,\n", - "* add more content connectors to allow more formats to be processed,\n", - "* build advanced index chains to get more semantical awareness into your system.\n", - "\n", - "Stay patient! We will cover it all ⚡ when we goo deeper.\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "source": [ - "## Prompt Chaining\n", - "\n", - "Prompt Chaining is a method that tries to avoid a lot of the current LLM weaknesses - no recent knowledge, no query results, no maths and so on. The concept is simple and compelling - a reasoning loop combined with external tools like a search engine, a Wolfram engine or a self-written command creates output which then is taken again as input for the next steps.\n", - "\n", - "You could consider it to be a self-writing workflow. A good example is [AgentGPT](https://agentgpt.reworkd.ai/de) (you need an OpenAI API Key), or the [Langchain Library](https://python.langchain.com/en/latest/) we are going to use in a minute.\n", - "\n", - "Prompt Chain image, taken from Maggie Appleton's Digital Garden:\n", - "![Prompt Chaining, taken from Maggie Appleton's Digital Garden](https://maggieappleton.com/images/posts/forest-talk/dft_54.jpeg)\n", - "\n" - ], - "metadata": { - "id": "SImn0AA2kinK" - } - }, - { - "cell_type": "markdown", - "source": [ - "### First-Time Preparations \n", - "\n", - "If you didn't try the Knowledge Hub experiments above, you have to run the commands in this section. Otherwhise you can skip this block. Make sure nevertheless that your Google Drive is mounted to the runtime.\n", - "\n", - "First, install the foundational libraries." - ], - "metadata": { - "id": "gdhtxiN6_R6Z" - } - }, - { - "cell_type": "code", - "source": [ - "!pip install langchain\n", - "!pip install openai" - ], - "metadata": { - "id": "mbBV8efrAQSo" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "Then you will need to store your OpenAI access key in the runtime environment." - ], - "metadata": { - "id": "zM7Pxet7BlAP" - } - }, - { - "cell_type": "code", - "source": [ - "def open_file(filepath):\n", - " with open(filepath, 'r', encoding='utf-8') as infile:\n", - " return infile.read()\n", - "\n", - "api_key = open_file('/content/drive/MyDrive/llama-openapikey.txt')\n", - "%set_env OPENAI_API_KEY=$api_key" - ], - "metadata": { - "id": "z9uvDj4SBqEm" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "### Getting warm with LangChain\n", - "\n", - "We follow the Quickstart Guide with a quick check whether OpenAI is running properly." - ], - "metadata": { - "id": "-7vQKNczF2V8" - } - }, - { - "cell_type": "code", - "source": [ - "from langchain.llms import OpenAI\n", - "\n", - "llm = OpenAI(temperature=0.9)\n", - "text = \"What would be a good catchy name for an alternative school that fosters self-determined learning and learner agency?\"\n", - "print(llm(text))\n" - ], - "metadata": { - "id": "dERVrxbyGHFK" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [], - "metadata": { - "id": "DbAVP4RZNr5Y" - } - }, - { - "cell_type": "markdown", - "source": [ - "#### Prompts\n", - "\n", - "Utilize the PromptTemplate capability to get rid of the hard-coded target:" - ], - "metadata": { - "id": "xdg2HPEFHuZM" - } - }, - { - "cell_type": "code", - "source": [ - "from langchain.prompts import PromptTemplate\n", - "\n", - "prompt = PromptTemplate(\n", - " input_variables=[\"target\"],\n", - " template=\"What is a good catchy name for {target}?\",\n", - ")\n", - "\n", - "print(prompt.format(target=\"an alternative school that fosters self-determined learning and learner agency\"))" - ], - "metadata": { - "id": "c59GqTWRH27B" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "Now we have the basics to build chains. A chain in LangChain is made up of links, which can be either primitives like LLMs or other chains.\n" - ], - "metadata": { - "id": "yndqYNpdIB8L" - } - }, - { - "cell_type": "code", - "source": [], - "metadata": { - "id": "XI87nvDHICQZ" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "later: https://gpt-index.readthedocs.io/en/latest/how_to/integrations/using_with_langchain.html" - ], - "metadata": { - "id": "BiHxmzHRNbea" - } - } - ], - "metadata": { - "colab": { - "provenance": [], - "authorship_tag": "ABX9TyPslbGzwqeY3+NOjehesaVa", - "include_colab_link": true - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - }, - "language_info": { - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VjbM5InfQjTM" + }, + "source": [ + "# Setting up a Hosting Provider\n", + "\n", + "If you have worked with Cloud providers, you know the setup of virtual machines. While for classic projects, a virtual machine is rather a convenience (quick setup, automated installation, secured environment etc.), it becomes a \"must\" for experiments with AI. Models, data, and processing needs grow so quick that maintaining your own setup is not feasible any longer - at least until you leave learning mode and decide to build your own AI factory 😅.\n", + "\n", + "For first steps, [Google Colab](https://colab.research.google.com/) is a good and easy to use option:\n", + "* Free for initial steps.\n", + "* Integrated with your Google Account and Drive, so you have a project store already.\n", + "* Integrated with Github, so you can easily maintain the development of your notebooks in an orderly fashion.\n", + "* Paid upgrades possible if you need more.\n", + "* Local runtime of Jupyter possible if you already have resources available ([Setup Info ](https://research.google.com/colaboratory/local-runtimes.html), not yet tried).\n", + "\n", + "Especially in the free environment, Colab has some performance and quality issues. You will learn pretty quick about broken tasks because your (virtual) hardware is not sufficient. So if you want to upgrade to Colab Pro or move to a more specialized provider, feel free to do so. I tried some few, but are not yet able to compare them systematically since I basically used only beginner's feature sets.\n", + "\n", + "* [RunPod](https://www.runpod.io/) - GPU Cloud with pre-setup environments\n", + "* [Paperspace](https://paperspace.com) - Notebooks, GPU, Deployment Services\n", + "* [HuggingFace](https://huggingface.co/) - Home of the Open Source Models, Development and Deployment ecosystem (you will need here an account in any case if you want to use one of the open models like Stable Diffusion or GPT-J).\n", + "\n", + "---\n", + "> **Keep an eye on where you commit to usage fees, there are many tools out there that require a funding source. Even if it is only some cents for initial experiments, consumption can get out of hand easily.**\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XLk0C-xbWdn_" + }, + "source": [ + "# Know Your Tools\n", + "\n", + "It's great that ChatGPT can write now software code for you, but what do you do with it? Today's tool chain maturity still requires that you integrate partial results into an end-to-end workstream. So you should at least roughly know what you are doing with the individual components.\n", + "\n", + "For developers this might be familiar turf to some extent, so you can quickly flip through this block. For everyone else, this chapter is not necessarily a roadblock before you can jump into AI joys, but it can't hurt to make yourself familiar with these concepts before or in parallel to the AI learning, depending how you learn best.\n", + "\n", + "## Git\n", + "git is the central repository for developer articats and platforms like Github have evolved to a complete tech ecosystem. I don't want to write just another git manual, there is plenty out there. Checkout some places to learn about the basic lifecycle, this is enough for our purposes.\n", + "\n", + "Some interesting learning pages are\n", + "* [Roger Dudler's git guide](https://rogerdudler.github.io/git-guide/index.html)\n", + "* [Intro videos for beginners](https://www.git-tower.com/learn/git/videos/), use the CLI version of the videos.\n", + "\n", + "## Jupyter Notebook\n", + "You're here, aren't you? So you already managed to see a notebook, and when we come to the action part, you will get interactive too. Let's stick to the headline \"documentation and coding environment\" as the mental model why we are here.\n", + "\n", + "No worries, we don't code yet. If you are on a Colab environment, let's just use a little predefined notebook integration to insert a codeblock semi-automatically. Click the folder on the left toolbar, then select the folder with the google drive symbol. **Do not** accept the access options. \n", + "\n", + "![gdrive-image](https://github.com/selfscrum/hello-ai/blob/main/images/gdrive.png?raw=1)\n", + "\n", + "This will cause Colab to add a code block below the currently selected one.\n", + "\n", + "![gdrive-block-image](https://github.com/selfscrum/hello-ai/blob/main/images/gdrive-block.png?raw=1)\n", + "\n", + "If you hover over the area between the square brackets, a play symbol appears. Click on it, and confirm all the warnings and acceptance screens. After some seconds, the code block (this is what it actually is) is executed successfully, indicated by a green checkmark. You know now that this block has run in this session. \n", + "\n", + "![gdrive-success-image](https://github.com/selfscrum/hello-ai/blob/main/images/gdrive-success.png?raw=1)\n", + "\n", + "If you refresh now the folder view in the left panel, you see the mounted drive folder which points to your private Google Drive space. This means you can access all files in Google Drive from within the Notebook. You might want to use it for session results, and data and model uploads. All assets that are meant to persist the session, should be stored directly in the notebook or as files in Github." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Gkg475CCwYbq" + }, + "source": [ + "\n", + "## Python\n", + "\n", + "I am not a professional developer and know Python only from occasional small coding sessions. What I see in most notebooks is some glue code or a sequence of setup steps for the technology we are going to use. So, if you have a rough understanding what coding with Python is, this won't hurt. If all goes well, you do not need to jump into the code but just have to start it - this is the beauty of Jupyter Notebooks, which can guide you through an interactive session without jumping in every tarpit.\n", + "\n", + "But if something goes wrong, you might get some error messages. It is surely beneficial if you can roughly interpret them, or check answers from Google to fix the problem.\n", + "\n", + "## Markdown\n", + "\n", + "You should be familiar with the markdown notation since this is how Jupyter Notebook texts are written. You find this supported in many tools like Github, Notion, too name some. It is easy and compelling and relieves you from the \"Learn MS Word\" burden. See [Markdown Guide](https://www.markdownguide.org/)\n", + "\n", + "## Image manipulation\n", + "\n", + "If you are interested in image processing, you might need some processing tools since current AI has quite some prerequisites about image formats etc. You can use any desktop app of your choice, or you can use web services like [Birme](https://birme.net) to support your batch workflow.\n", + "\n", + "An increasingly interesting combination is the creation of quick low-scale images by the generation model and an AI-based scale-up app that creates a high-res, high-quality image afterwards.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "J8iqqCuWH3d9" + }, + "source": [ + "# Find your Heros\n", + "\n", + "Learning alone is not how you get up to speed. Find a learning group to discuss your targets, your plans, your issues, and to celebrate your successes. This could be someone from your surroundings, or maybe you find a discord forum around one of the tools we are going to see.\n", + "\n", + "At least you should tune into some youtube videos to get some basic conceptual understanding. As always for a new topic, it's hard to know what you are looking for, so you have to try out some channels to find the proper ones that fit your needs and support you best.\n", + "\n", + "I learned from (in order of appearance on my radar)\n", + "* [Kris Kashtanova](https://www.kris.art/), an early adoptor of image creation on Midjourney.\n", + "* [Sebastian Kamph](https://www.youtube.com/@sebastiankamph) about Stable Diffusion, deforum and Automatic1111.\n", + "* [Amelia Player aka PromptMuse](https://www.youtube.com/@promptmuse) about various image and video processing workflows.\n", + "* [David Shapiro](https://www.youtube.com/@DavidShapiroAutomator) GPT3 and ChatGPT experiments with Python. He is always \"5 minutes ahead\" of you, so if you like to follow searchers, this is the one for you.\n", + "* [Olivio Sarikas](https://www.youtube.com/@OlivioSarikas) for prompt engineering details for imaging.\n", + "* [Maggie Appleton](https://maggieappleton.com/forest-talk), Designer and anthropologist with some impressive and well-researched essays about the topic." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gTj9i_BzjvmN" + }, + "source": [ + "# Start experimenting\n", + "\n", + "What are your most urgent questions? how do you enter the scene? This is hard to answer generally since everyone comes from a different path and has different knowledge.\n", + "\n", + "If you are completely new to AI and coding, you might want to start easy with\n", + "* [ChatGPT](https://chat.openai.com/). Very low entry barrier, you get a chat window and can start talking to the ai without any background knowledge.\n", + "* [Midjourney](https://midjourney.com/). Image generation site. To use it, you need to have a Discord account, which is for people new to the scene something like \"Teams on steroids\". It has its roots in the gamer scene but is meanwhile a popular community platform for many new initiatives. Follow the guidelines there to sign up, then \"Join the Beta\" on Midjourney." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "82Ix-cma5pld" + }, + "source": [ + "# Advanced Experiments\n", + "\n", + "> The current cambrian explosion of advances in the AI ecosystem is breathtaking. New tools are released by the hour, and many libraries I use here are under steady development. Some breaks might occur. I try to keep everything somewhat up to date. If you come across an error please let me know by creating an issue [here](https://github.com/selfscrum/hello-ai/issues)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1n6SheTlRmI9" + }, + "source": [ + "## Build a Knowledge Hub Around a GPT Model\n", + "As impressive GPT is, it has one big shortcoming:\n", + "\n", + "---\n", + "> **You can't infuse new knowledge easily**\n", + "---\n", + "\n", + "This might be surprising, isn't this what a Large Language Model (LLM) is all about? Yes and no. Training of an LLM is a long-during expensive task which requires vast amounts of texts. A typical GPT user cannot afford these resources, so that is a limiting factor. In addition, training takes a lot of time, so scenarios where your knowledge is varying more often (e.g. look at a typical IT curriculum, or even worse, at a marketing customer pipeline), you are at a loss with that approach.\n", + "\n", + "Another strategy is to built semantic indices of your current knowledge sets (also known as **vectors**, since each **chunk** (piece of knowledge) can be mapped to a mathematical multidemensional vector representing the information in the knowledge piece. If you then ask something, this question is vectorized as well and the vector store can easily find related answers by comparing the vectors. These answers can then be passed into GPT to phrase a good response to your original request.\n", + "\n", + "![vectors](https://github.com/selfscrum/hello-ai/blob/main/images/vectors.png?raw=1)\n", + "\n", + "That's what we are going to build here. A new open source project named LlamaIndex is supporting just that, and, even better, has already a lot of connectors to typical knowledge stores, like databases, Slack, Notion etc. Let's see how this works out!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bYR9IKCLMoVh" + }, + "source": [ + "### Install LlamaIndex and do a Quick Starter Tour\n", + "\n", + "We can follow the tutorial from [here](https://gpt-index.readthedocs.io/en/latest/getting_started/installation.html):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "qr_pcGENUwQn" + }, + "outputs": [], + "source": [ + "!pip install --upgrade pip\n", + "!pip install llama-index" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XMWZkGn-g_E0" + }, + "source": [ + "I store my openapi key on the private part of My Google Drive. This removes the need to edit a public notebook every time. The file must contain only the key (no newline!)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gomXEMJCVWYP" + }, + "outputs": [], + "source": [ + "def open_file(filepath):\n", + " with open(filepath, 'r', encoding='utf-8') as infile:\n", + " return infile.read()\n", + "\n", + "api_key = open_file('/content/drive/MyDrive/llama-openapikey.txt')\n", + "%set_env OPENAI_API_KEY=$api_key" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JXbg-xPMW_1m" + }, + "source": [ + "Now you can install the current release of LlamaIndex (they just recently renamed, still sometimes referred to as `gpt_index`)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "REuJM75AXNi3" + }, + "outputs": [], + "source": [ + "!git clone https://github.com/jerryjliu/gpt_index.git" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oLu3_8J5b4wu" + }, + "source": [ + "Start the code of an example to check whether the service is running." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nEfG1DvSY0Dk" + }, + "outputs": [], + "source": [ + "from llama_index import GPTVectorStoreIndex, SimpleDirectoryReader\n", + "\n", + "%cd /content/gpt_index/examples/paul_graham_essay/\n", + "\n", + "documents = SimpleDirectoryReader('data').load_data()\n", + "index = GPTVectorStoreIndex.from_documents(documents)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XuX3r3pRfnyb" + }, + "source": [ + "Now you can query the essay." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bV6pbFP0co4Z" + }, + "outputs": [], + "source": [ + "query_engine = index.as_query_engine()\n", + "response = query_engine.query(\"What did the author do growing up?\")\n", + "print(response)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vXPPFDH15_BX" + }, + "source": [ + "### Process a book to get an online query engine\n", + "\n", + "In the example above we just read some 300 statements from that essay, and were able to give some satisfying answers.\n", + "\n", + "Let's now increase the stakes a little. I am going to import a PDF ebook (\"Practical Process Automation\" of Bernd Ruecker, about 300 pages, 18 MB) in a simple vector index - this is the general purpose index you can use for document retrieval, as we did before.\n", + "\n", + "You can do that to if you have the PDF, it was available free as a sponsored download for some time (but of course you can use any other text-based PDF as well. Make sure it's not too short.)\n", + "\n", + "I upload the document to my Google Drive and load it from there into the index.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TpYCx-9O8kZc" + }, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from llama_index import download_loader\n", + "from llama_index import GPTVectorStoreIndex\n", + "\n", + "PDFReader = download_loader(\"PDFReader\")\n", + "\n", + "loader = PDFReader()\n", + "pdfdocuments = loader.load_data(file=Path('/content/drive/MyDrive/pdfs/PracticalProcessAutomation_Camunda.pdf'))\n", + "pdfindex = GPTVectorStoreIndex.from_documents(pdfdocuments)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7ODaNXrN8_L3" + }, + "source": [ + "That was quite quick! Now we can query the book's knowledge." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "9mE97HN_8_mG" + }, + "outputs": [], + "source": [ + "query_engine = pdfindex.as_query_engine()\n", + "response = query_engine.query(\"Should we use Powerpoint for Process Modeling? Please name better alternatives.\")\n", + "print(response)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hGGyMZwtMyp4" + }, + "source": [ + "### Process a second book \n", + "\n", + "Can we increase our knowledge base? For sure! We could just load another document into the index. I choose the \"Data Mesh\" book of Zhamak Dehghani (another sponsored copy)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "i32X06UHPDPI" + }, + "outputs": [], + "source": [ + "pdfdocs2 = loader.load_data(file=Path('/content/drive/MyDrive/pdfs/DataMesh.pdf'))\n", + "for doc in pdfdocs2:\n", + " pdfindex.insert(doc)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "G7vgR4V2PcC1" + }, + "source": [ + "This takes a little longer, the book has nearly 400 pages and is 55 MB in size. After a minute we are ready, and the same query as above might yield better results, especially when we ask now about topics that belong to both books, e.g. \"How can we use Practical Process Automation to implement Data Mesh?\" " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Vx39oARHNWtq" + }, + "source": [ + "This works all pretty well ✨, but we just scratched the surface. \n", + "\n", + "What we technically did now is loading both books (chopped into small chunks that the language model can process) into a list of vectors that we can search and pass on to a summarized answer. LlamaIndex does this for us in a nice and easy way, while hiding the complexities of the workflow. The structure is still simple though, and we have lots of option unrevealed.\n", + "\n", + "![llamaindexoptions](https://github.com/selfscrum/hello-ai/blob/main/images/llamaindex-options.png?raw=1)\n", + "\n", + "So, we must decide how we can improve our knowledge base next:\n", + "\n", + "* Increase Token Size, or make it variable, to allow longer answers,\n", + "* Allow other language models than the default, or provide parameter tuning tweaks,\n", + "* Use another index structure to support hierarchical content in a better way,\n", + "* add more content connectors to allow more formats to be processed,\n", + "* build advanced index chains to get more semantical awareness into your system.\n", + "\n", + "Stay patient! We will cover it all ⚡ when we goo deeper.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SImn0AA2kinK" + }, + "source": [ + "## Prompt Chaining\n", + "\n", + "Prompt Chaining is a method that tries to avoid a lot of the current LLM weaknesses - no recent knowledge, no query results, no maths and so on. The concept is simple and compelling - a reasoning loop combined with external tools like a search engine, a Wolfram engine or a self-written command creates output which then is taken again as input for the next steps.\n", + "\n", + "You could consider it to be a self-writing workflow. A good example is [AgentGPT](https://agentgpt.reworkd.ai/de) (you need an OpenAI API Key), or the [Langchain Library](https://python.langchain.com/en/latest/) we are going to use in a minute.\n", + "\n", + "Prompt Chain image, taken from Maggie Appleton's Digital Garden:\n", + "![Prompt Chaining, taken from Maggie Appleton's Digital Garden](https://maggieappleton.com/images/posts/forest-talk/dft_54.jpeg)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gdhtxiN6_R6Z" + }, + "source": [ + "### First-Time Preparations \n", + "\n", + "If you didn't try the Knowledge Hub experiments above, you have to run the commands in this section. Otherwhise you can skip this block. Make sure nevertheless that your Google Drive is mounted to the runtime.\n", + "\n", + "First, install the foundational libraries." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mbBV8efrAQSo" + }, + "outputs": [], + "source": [ + "!pip install langchain\n", + "!pip install openai" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zM7Pxet7BlAP" + }, + "source": [ + "Then you will need to store your OpenAI access key in the runtime environment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "z9uvDj4SBqEm" + }, + "outputs": [], + "source": [ + "def open_file(filepath):\n", + " with open(filepath, 'r', encoding='utf-8') as infile:\n", + " return infile.read()\n", + "\n", + "api_key = open_file('/content/drive/MyDrive/llama-openapikey.txt')\n", + "%set_env OPENAI_API_KEY=$api_key" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-7vQKNczF2V8" + }, + "source": [ + "### Getting warm with LangChain\n", + "\n", + "We follow the Quickstart Guide with a quick check whether OpenAI is running properly." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dERVrxbyGHFK" + }, + "outputs": [], + "source": [ + "from langchain.llms import OpenAI\n", + "\n", + "llm = OpenAI(temperature=0.9)\n", + "text = \"What would be a good catchy name for an alternative school that fosters self-determined learning and learner agency?\"\n", + "print(llm(text))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DbAVP4RZNr5Y" + }, + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xdg2HPEFHuZM" + }, + "source": [ + "#### Prompts\n", + "\n", + "Utilize the PromptTemplate capability to get rid of the hard-coded target:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "c59GqTWRH27B" + }, + "outputs": [], + "source": [ + "from langchain.prompts import PromptTemplate\n", + "\n", + "prompt = PromptTemplate(\n", + " input_variables=[\"target\"],\n", + " template=\"What is a good catchy name for {target}?\",\n", + ")\n", + "\n", + "print(prompt.format(target=\"an alternative school that fosters self-determined learning and learner agency\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yndqYNpdIB8L" + }, + "source": [ + "Now we have the basics to build chains. A chain in LangChain is made up of links, which can be either primitives like LLMs or other chains.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XI87nvDHICQZ" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BiHxmzHRNbea" + }, + "source": [ + "later: https://gpt-index.readthedocs.io/en/latest/how_to/integrations/using_with_langchain.html" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyPslbGzwqeY3+NOjehesaVa", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}