https://soshnikov.com/Dmitry Soshnikov AKA shwars2024-03-03T07:55:57+00:00I am Associate Professor at Higher School of Economics and Moscow Aviation Institute, Technical Lead of AI Lab and HSE School or Art and Design, ex-Microsoft for 16 year, doing Technical Evangelism, AI/ML and F#. I teach AI and Functional/Logic Programming. Passionate father, Digital Artist, Gun Fu Cha master, etc. Opinions are my own and not those of my employers.Dmitri Soshnikovhttp://soshnikov.com/Jekyllhttps://soshnikov.com/ai/santify-yourself-ru/Сантафицируйся!2023-12-20T00:00:00+00:00Dmitri Soshnikovhttp://soshnikov.com/В преддверии Нового года хочется делать какие-то рождественские безумия. Я вот сделал бота, который превратит вас в Деда Мороза или Санта Клауса. А какой за этим стоит смысл - читайте в посте.<p><a href="http://t.me/santify_me_bot"><img src="/images/blog/santify.jpg" alt="Sanify_Yourself" /></a></p>
<p><strong>TL;DR:</strong> Добавляйте себе телеграм-бота <a href="http://t.me/santify_me_bot">http://t.me/santify_me_bot</a>, посылайте ему свою портретную фотографию - и он сделает из вас Деда Мороза или Санта-Клауса (ну а для девушек… попробуйте угадать, кого)! А в чем глубокий смысл этой процедуры - расскажу в этом посте.</p>
<h2 id="санты-среди-нас">Санты среди нас</h2>
<p>Есть такая теория, что Дед Мороз (или Санта Клаус) существует! Возможно, Ваши дети разделяют эту теорию и верят в волшебство. А что же происходит на самом деле?</p>
<p>В своё время Лесли Раш из Техаса, учительница истории, <a href="https://mel.fm/novosti/4691203-santa">нашла очень хорошее объяснение</a> <a href="https://www.washingtonpost.com/news/inspired-life/wp/2016/12/19/the-story-behind-the-beautiful-way-this-mom-told-her-sons-the-truth-about-santa/">(англ)</a>, которое передавалось в их семье из поколения в поколение. Если коротко, вот оно:</p>
<p>Мы с вами видим много Дедов Морозов каждый Новый год, и все они выглядят как обычные люди, наряженные в костюмы. Значит ли это что, Дедов Морозов не существует?</p>
<p>Нет, это просто значит, что ими становятся обычные люди! Когда ребенок вырастает, он уже не так нуждается во внешней поддержке и волшебстве, и может сам начать создавать это волшебство для других людей. Когда ребенок вырастает, он может сам стать Сантой, и начать безвозмездно (а часто и тайно) дарить рождественское тепло окружающим. И мир станет лучше!</p>
<h2 id="сантафикация-или-одедмороживание">Сантафикация или Одедмороживание?</h2>
<p>Именно такой процесс символизирует <a href="http://t.me/santify_me_bot">бот-сантафикатор</a>. Посылая ему свою портретную фотографию вы как бы визуализируете себя в роли Деда Мороза. Попробуйте несколько раз, выберите понравившуюся фото и сохраните себе в телефон. Потом, каждый раз, глядя на эту фотографию, вы будете вспоминать о том, что в вас есть немножко Санты - пусть это подталкивает вас делать мир вокруг себя лучше и наполнять его еще большим количеством волшебства!</p>
<p>Сантафицируйтесь сами и сантафицируйте своих друзей, поделившись с ними ссылкой на бота.</p>
<p>С наступающим!</p>
2023-12-20T00:00:00+00:00https://soshnikov.com/ai/yogpt-using-chatgpt-and-other-llms-from-command-line/yogpt: Using ChatGPT and Other LLMs from the Command Line2023-11-18T00:00:00+00:00Dmitri Soshnikovhttp://soshnikov.com/Recently, we tend to use chat language models more and more in our everyday activities. If you are a traditional office worker, using it through web chat interface or co-pilot seems natural. However, for developers, who spend a lot of their time on the command line, having terminal interface for chat models is totally useful.<p>There are already some projects that give command-line interface for ChatGPT, for example, <a href="https://github.com/fuyufjh/heygpt">heygpt</a>. However, it only gives you access to OpenAI ChatGPT model, while I wanted to have access to other models, such as Yandex GPT and GigaChat. Moreover, heygpt is implemented in Rust, which makes it pain to install for those of us who live in Python ecosystem.</p>
<h2 id="welcome-yogpt">Welcome <strong>yogpt</strong></h2>
<p>This forced me to create my own command-line GPT tool called <a href="https://github.com/shwars/yogpt"><strong>yogpt</strong></a> over the weekend. Here are the main principles that I followed:</p>
<ul>
<li>The tool should be pip-installable. Most people nowadays would have Python command-line on their computer, and running <code class="language-plaintext highlighter-rouge">pip install yogpt</code> would make the tool immediately available.</li>
<li>It should support any chat model supported by LangChain - making a plethora of models readily available, including original ChatGPT, GigaChat, YandexGPT, etc.</li>
</ul>
<p>There are a few ways <code class="language-plaintext highlighter-rouge">yogpt</code> can be used:</p>
<ul>
<li>By <strong>asking the question to GPT directly on the command line</strong>, for example:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>yogpt What is the 10th digit of Pi?
</code></pre></div> </div>
</li>
<li>By <strong>piping stdin</strong> into <code class="language-plaintext highlighter-rouge">yogpt</code>. If <code class="language-plaintext highlighter-rouge">yogpt</code> understands that it is invoked in the pipe, it will automatically take pipe as input. You can also explicitly specify <code class="language-plaintext highlighter-rouge">-</code> in the command-line, for example:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo </span>What is the 10th digit of Pi | yogpt -
</code></pre></div> </div>
</li>
<li>Calling <code class="language-plaintext highlighter-rouge">yogpt</code> without input initiates <strong>console chat</strong>, and you can talk to the model interactively.</li>
<li>Sometimes it makes sense to further chat with <code class="language-plaintext highlighter-rouge">yogpt</code> after providing it with some input. For example, you can ask it to read Python program, and then answer your questions about it. In this case you may specify <code class="language-plaintext highlighter-rouge">-c</code>/<code class="language-plaintext highlighter-rouge">--chat</code> flag. In this case it will consume input as first utterance to the bot, and then initiate a <strong>follow-up chat</strong>, in which you can ask for more details. For example, you can ask GPT to analyze a Python program (<a href="https://github.com/shwars/PresentationDemos/blob/master/PiterPy2023/game.py">this one</a>):</li>
</ul>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ yogpt -s "You are a software expert. Please read python program
provided and be ready to answer questions on this program."
-c @game.py
This program is a simple implementation of the game Space Invaders
using the Pygame library in Python. It includes a player-controlled
spaceship that can move left and right, shoot bullets, and destroy
enemy spaceships. The player earns points for each enemy spaceship
destroyed.
U> How is collision detection implemented?
AI> Collision detection in this program is implemented using the
`groupcollide` function provided by the Pygame library. The
`groupcollide` function is used to detect collisions between
two sprite groups and manage the sprites that collide.
U> ^C
</code></pre></div></div>
<p>The example above also shows that you can use <code class="language-plaintext highlighter-rouge">@filename</code> syntax to get input from a file. Also, we have specified <em>system prompt</em> here via <code class="language-plaintext highlighter-rouge">-s</code> parameter, instructing the model what it should do.</p>
<h2 id="system-prompt-and-prompt-templates">System Prompt and Prompt Templates</h2>
<p>In many cases, we want to instruct the model to do something to the data that we provide to it as part of the prompt. For example, if we want to use GPT to translate a program from one programming language to another, we need to provide those instructions. This can be done in two ways:</p>
<ul>
<li>By specifying <strong>system prompt</strong> via <code class="language-plaintext highlighter-rouge">-s</code>/<code class="language-plaintext highlighter-rouge">--system</code> parameter:</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo </span>print<span class="o">([</span>2<span class="k">**</span>i <span class="k">for </span>i <span class="k">in </span>range<span class="o">(</span>10<span class="o">)])</span> |
yogpt <span class="nt">-s</span> <span class="s2">"Translate the program that you are given as input to C++"</span>
</code></pre></div></div>
<p>Here is the result you are likely to get:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><iostream></span><span class="cp">
#include</span> <span class="cpf"><vector></span><span class="cp">
#include</span> <span class="cpf"><cmath></span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">result</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">result</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">pow</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">i</span><span class="p">));</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">result</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="n">result</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o"><<</span> <span class="s">" "</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>By using <strong>prompt template</strong> via <code class="language-plaintext highlighter-rouge">-p</code>/<code class="language-plaintext highlighter-rouge">--template</code> parameter:</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo </span>print<span class="o">([</span>2<span class="k">**</span>i <span class="k">for </span>i <span class="k">in </span>range<span class="o">(</span>10<span class="o">)])</span> |
yogpt <span class="nt">-p</span> <span class="s2">"Translate the program that you are given below in
double square brackets to {param_1}:</span><span class="se">\n</span><span class="s2">[[{}]]. Provide only
program text as output."</span> <span class="nt">-1</span> Javascript
</code></pre></div></div>
<p>This gives the following result:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">({</span><span class="na">length</span><span class="p">:</span> <span class="mi">10</span><span class="p">},</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=></span> <span class="mi">2</span> <span class="o">**</span> <span class="nx">i</span><span class="p">));</span>
</code></pre></div></div>
<p>You can see that prompt template provides more flexibility, because you can use different prompt engineering techniques such as delimiting the input explicitly, and fine-tuning the prompt (in our case I have added <em>Provide only program text as output</em> to avoid getting some additional explanation on what the program does).</p>
<p>Also, I have used additional parameter here, which specifies destination programming language. By using constructions from <code class="language-plaintext highlighter-rouge">{param_1}</code> to <code class="language-plaintext highlighter-rouge">{param_3}</code> in the template, I can then specify those parameters by <code class="language-plaintext highlighter-rouge">-1</code> to <code class="language-plaintext highlighter-rouge">-3</code> command options. This is especially useful if I am getting a template from a file or config, and cannot easily adjust it on the command line.</p>
<p>For both system prompt and prompt template, we can specify <code class="language-plaintext highlighter-rouge">@filename</code> in order to read prompt from file. However, even better way is to put prompt templates that you often use into config file, as described below.</p>
<h2 id="installation-and-configuration">Installation and Configuration</h2>
<p>Now that you have seen a few examples of using <strong>yogpt</strong>, you are probably wondering how to start using it.</p>
<p>As I mentioned, installing the model on your local computer, provided that you have Python 3.10+ tooling available, can be done using:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>pip <span class="nb">install </span>yogpt
</code></pre></div></div>
<blockquote>
<p>It is important to have a version of Python>=3.10, because that is what LangChain requires.</p>
</blockquote>
<p>If you just do pip-install without any configuration, the tool would try to use <a href="https://github.com/xtekky/gpt4free">GPT4Free</a> project to find some way to access ChatGPT model online. However, I do not recommend doing so in the long run - please spend some time configuring your own list of models and your own credentials.</p>
<p>Configuration is done by creating a <code class="language-plaintext highlighter-rouge">.yogpt.config.json</code> file in your home directory (which is typically <code class="language-plaintext highlighter-rouge">c:\users\<username></code> on Windows, or <code class="language-plaintext highlighter-rouge">/home/<username></code> on Linux). This is a JSON file that allows you to specify models you want to use, including your personal API keys. For example, here is the file that specifies Yandex GPT and GigaChat models:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"models"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"yagpt"</span><span class="p">,</span><span class="w">
</span><span class="nl">"classname"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"langchain.chat_models.ChatYandexGPT"</span><span class="p">,</span><span class="w">
</span><span class="nl">"default"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"params"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"api_key"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"..."</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"gc"</span><span class="p">,</span><span class="w">
</span><span class="nl">"classname"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"langchain.chat_models.GigaChat"</span><span class="p">,</span><span class="w">
</span><span class="nl">"params"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"credentials"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"... "</span><span class="p">,</span><span class="w">
</span><span class="nl">"verify_ssl_certs"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]}</span><span class="w">
</span></code></pre></div></div>
<blockquote>
<p>You can check out sample <code class="language-plaintext highlighter-rouge">.yogpt.config.json</code> file in the project repository <a href="https://github.com/shwars/yogpt/blob/main/sample_config.json">here</a> and use it as a starting point.</p>
</blockquote>
<p>As you can see, for each model we supply class name of the model class (which can be part LangChain, but you can also specify other compatible models), as well as parameters that we pass to the class constructor. We can also mark any model in the config file as default.</p>
<p>Having defined your models in config, you can specify the model when calling <code class="language-plaintext highlighter-rouge">yogpt</code> like this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>yogpt <span class="nt">-m</span> yagpt Привет, расскажи анекдот про число пи!
Почему число Пи такое большое? Потому что оно округляет!
</code></pre></div></div>
<p>(<em>The text here is in Russian, because Yandex GPT model works best with this language</em>)</p>
<p>Also, in the same config file you can define prompt templates and system prompts that you often use. Suppose you often want to use <code class="language-plaintext highlighter-rouge">yogpt</code> to translate text into different languages, in this case you can define the following template in <code class="language-plaintext highlighter-rouge">templates</code> section:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"translate"</span><span class="p">,</span><span class="w">
</span><span class="nl">"template"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Please, translate the text in double square brackets
below into the {param_1} language. Here is the text:</span><span class="se">\n</span><span class="s2">[[{}]]"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>In which case translation would be done using the command:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ echo How do you do? | yogpt -p translate -1 german -t 0.01
Wie geht es Ihnen?
</code></pre></div></div>
<p>Here I have specified the <code class="language-plaintext highlighter-rouge">--temperature</code> or <code class="language-plaintext highlighter-rouge">-t</code> parameter, in order to get less hallucinations in the output (i.e. more accurate translation). If I want the model to be more creative, I would probably specify higher value (from 0 to 1):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ yogpt -t 0.6 Invent 5 funny names for a cat of Python developer.
1. Bytey Whiskers
2. Code Paws
3. Monty Purthon
4. Snaketail Scriptmeow
5. PyFluff Constrictor
</code></pre></div></div>
<h2 id="summarizing-blog-posts">Summarizing blog posts</h2>
<p>In the text about, you have seen a few examples of using <code class="language-plaintext highlighter-rouge">yogpt</code> in tasks from understanding computer programs to translation. There are many possible example of command-line usage for GPT, from translating LaTeX formula to Python, to re-writing blog posts in a different tone of voice. For example, <a href="https://lpetrov.cc/AI-math/">this blog post</a> contains a lot of nice examples of using GPT in mathematician’s everyday life, and most of them are from command line.</p>
<p>Let’s stick to the latter example of processing blog posts. For some not-so-long blog posts (which are short enough to fit into context window of current language models), I try to use <code class="language-plaintext highlighter-rouge">yogpt</code> for translation. I have the following prompt <code class="language-plaintext highlighter-rouge">blogtran</code> defined in my config file: <em>I want to translate my blog post written in Jekyll markdown format into Russian. Please leave the configuration section intact, and translate the rest of the text, leaving markdown syntax. The blog post is provided below: {}</em>, which allows me to use:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ yogpt -p blogtran @blog.md > blog_en.md
</code></pre></div></div>
<p>It is known that GPTs provide better translation than specialized translation systems, but they have shorter context, and sometimes can hallucinate.</p>
<p>Much better use case for GPTs are <strong>text summarization</strong>, which allows us to extract most important information from the blog post. The prompt I may use for summarization is the following: <em>Please, read the post below in Jekyll Markdown and write its short summary. Output results in JSON format with “title” and “summary” fields. Here is the post:{}</em>. I will put it into <code class="language-plaintext highlighter-rouge">summarize.txt</code>, and then loop over blog posts to extract their summary:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for %c in (*.md) do;
yogpt -m yagpt -p @summarize.txt @%c >> result.json
</code></pre></div></div>
<p>This will create <code class="language-plaintext highlighter-rouge">result.json</code> file, which will not exactly be well-formed JSON, but will look similar:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Neurogenerative Models and the Future of Art"</span><span class="p">,</span><span class="w">
</span><span class="nl">"summary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"The post discusses the latest developments in neurogenerative models, focusing on Stable Diffusion. It explores the capabilities of text-to-image models, showcasing examples and speculating on their impact on the art world. The author highlights the release of Stable Diffusion's code and weights, emphasizing its significance for artists. The post also delves into prompt engineering, creative uses of neural generation, and the potential for AI to enhance the artistic process. It concludes with new ways to experience neural art, including neurogenerative streams, integration into art objects, and the concept of neurogenerative parties."</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="err">`</span><span class="w"> </span><span class="err">``json</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Never Trust a Neural Network!"</span><span class="p">,</span><span class="w">
</span><span class="nl">"summary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"The post highlights the untrustworthiness of ChatGPT and other neural network models for providing accurate information. It illustrates this point by requesting absurd information from ChatGPT, such as reasons why eating cucumbers leads to sleeping disorders or why learning mathematics can be deadly. The post concludes with a mix of accurate and inaccurate historical information generated by ChatGPT, emphasizing the model's limitations and the need for skepticism."</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="err">``</span><span class="w"> </span><span class="err">`</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Creating Domain-Oriented Chatbots using LangChain and Yandex GPT"</span><span class="p">,</span><span class="w">
</span><span class="nl">"summary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"The article discusses the creation of domain-specific chatbots using the Retrieval-Augmented Generation approach with LangChain and Yandex GPT. It covers steps such as converting video to text, breaking text into fragments, calculating embeddings, saving documents to a vector database, implementing retrieval-augmented generation, and context transformation. It emphasizes the use of LangChain for various tasks and demonstrates how to build a question-and-answer bot based on a video collection."</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>In some of the cases this malformed JSON includes markdown formatting elements, and it lacks grouping of all individual JSON objects into one list. However, it does not matter if all we want to do is further process this with GPT, because LLM can handle slight deviations from JSON format. For example, to render this into HTML table, we can just say:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ yogpt -p "Please take a list of JSON objects with title and
summary fields and represent them as HTML table"
@result.json > result.html
</code></pre></div></div>
<p>And we will get HTML that can be used to display the summary:</p>
<table>
<tr>
<th>Title</th>
<th>Summary</th>
</tr>
<tr>
<td>Neurogenerative Models and the Future of Art</td>
<td>The post discusses the latest developments in neurogenerative models, focusing on Stable Diffusion. It explores the capabilities of text-to-image models, showcasing examples and speculating on their impact on the art world. The author highlights the release of Stable Diffusion's code and weights, emphasizing its significance for artists. The post also delves into prompt engineering, creative uses of neural generation, and the potential for AI to enhance the artistic process. It concludes with new ways to experience neural art, including neurogenerative streams, integration into art objects, and the concept of neurogenerative parties.</td>
</tr>
<tr>
<td>Never Trust a Neural Network!</td>
<td>The post highlights the untrustworthiness of ChatGPT and other neural network models for providing accurate information. It illustrates this point by requesting absurd information from ChatGPT, such as reasons why eating cucumbers leads to sleeping disorders or why learning mathematics can be deadly. The post concludes with a mix of accurate and inaccurate historical information generated by ChatGPT, emphasizing the model's limitations and the need for skepticism.</td>
</tr>
<tr>
<td>Creating Domain-Oriented Chatbots using LangChain and Yandex GPT</td>
<td>The article discusses the creation of domain-specific chatbots using the Retrieval-Augmented Generation approach with LangChain and Yandex GPT. It covers steps such as converting video to text, breaking text into fragments, calculating embeddings, saving documents to a vector database, implementing retrieval-augmented generation, and context transformation. It emphasizes the use of LangChain for various tasks and demonstrates how to build a question-and-answer bot based on a video collection.</td>
</tr>
</table>
<p>Of course, we can also ask GPT to represent this as markdown table, or as sequence of SQL statements to insert the data into the database.</p>
<p>Of course, instead of writing a blog summary, I can ask GPT to produce a selling text for it, which I can then put on the front page as a banner. I just need to adjust the prompt a little bit: <em>Please, read the post below in Jekyll Markdown and write a short motivational text, telling people why should they read it. Output results in JSON format with “title” and “summary” fields. Output pure json, without any decorations. Here is the post:{}</em>. After repeating all the steps above with the new prompt, the result will be:</p>
<table>
<tr>
<th>Title</th>
<th>Summary</th>
</tr>
<tr>
<td>Unlocking Creativity: Exploring the World of Neurogenerative Art</td>
<td>Dive into the fascinating realm of neurogenerative art with 'Neurogenerative Models and the Future of Art.' This post unveils the latest text-to-image models, such as Stable Diffusion, that can produce stunning and diverse artworks based on text prompts. Discover how AI can mimic various artistic styles, generate celebrity portraits, and even imagine abstract concepts like love and loneliness. Explore the possibilities of using neural generation for inspiration, drawing artifacts, and education. Join the neurogenerative art movement by attending neurogenerative parties, integrating neural generation into art objects, or experiencing curated neurogenerative streams. Unleash your creativity and explore the new frontier where human ingenuity collaborates with AI innovation!</td>
</tr>
<tr>
<td>Why You Should Never Trust a Neural Network</td>
<td>Explore the pitfalls of relying on ChatGPT and other neural network models for accurate information. Uncover the humorous and sometimes absurd responses when asking the model about topics like cucumbers causing sleeping disorders or the dangers of learning mathematics. Discover the importance of critical thinking and not taking the model's responses at face value. The post navigates through various scenarios, shedding light on the limitations and occasional unpredictability of generative AI.</td>
</tr>
<tr>
<td>Unlock the Power of Chatbots with LangChain and Yandex GPT</td>
<td>Discover the transformative potential of Retrieval-Augmented Generation in chatbots! Learn how to leverage LangChain framework and Yandex GPT to create domain-specific question-answer models. Dive into the world of smart chatbots, where the synergy of large language models and domain knowledge enhances user interactions. Uncover the secrets of creating intelligent bots that can provide detailed responses to specific topics, making your conversational agents more knowledgeable and engaging. Explore the step-by-step guide and unleash the capabilities of cutting-edge technology in the realm of chatbot development.</td>
</tr>
</table>
<p>Keep in mind that those HTML tables, including formatting, were created automatically from original blog posts using just two command lines in a matter of minutes!</p>
<h2 id="takeaway">Takeaway</h2>
<p>I think the examples shown above demonstrate the true transformative power of GPT models, which you can now freely use from the command-line! In case you like <code class="language-plaintext highlighter-rouge">yogpt</code>, fell free to leave your start, comments and suggestions on the <a href="http://github.com/shwars/yogpt">GitHub page</a>. And if you have some ideas on how to improve the tool - the best way to do it would be through a pull request, because I do not have much time to spend on improving this tool myself, but would welcome any help!</p>
2023-11-18T00:00:00+00:00https://soshnikov.com/ai/yogpt-using-chatgpt-and-other-llms-from-command-line-ru/yogpt: Используем ChatGPT и другие языковые модели из командной строки2023-11-18T00:00:00+00:00Dmitri Soshnikovhttp://soshnikov.com/В последнее время мы все чаще используем разговорные языковые модели в нашей повседневной деятельности. Если вы обычный офисный работник, то вы скорее всего используете ChatGPT через стандартный веб-интерфейс чата, или через Copilot. Однако для разработчиков, которые проводят много времени в командной строке, очень нужна возможность использовать ChatGPT прямо в терминале.<p>Уже есть несколько проектов, которые предоставляют интерфейс командной строки для ChatGPT, например, <a href="https://github.com/fuyufjh/heygpt">heygpt</a>. Однако этот проект дает вам доступ только к модели OpenAI ChatGPT, в то время как мне хотелось иметь доступ к другим моделям, таким, как Yandex GPT и GigaChat. Более того, heygpt реализован в Rust, что затрудняет его установку для тех из нас, кто живет в экосистеме Python.</p>
<h2 id="добро-пожаловать-в-yogpt">Добро пожаловать в <strong>yogpt</strong>!</h2>
<p>В результате я решил потратить выходные и сделать свой собственный инструмент для работы с разными GPT из командной строки под названием <a href="https://github.com/shwars/yogpt"><strong>yogpt</strong></a>. Вот основные принципы, которым я следовал при его разработке:</p>
<ul>
<li>Инструмент должен быть доступен для установки с помощью pip. В наши дни у большинства сознательных людей на компьютере уже есть командная строка Python, и достаточно сказать <code class="language-plaintext highlighter-rouge">pip install yogpt</code>, чтобы начать пользоваться <code class="language-plaintext highlighter-rouge">yogpt</code>.</li>
<li>Он должен поддерживать любую разговорную модель, доступную в LangChain, что открывает нам немедленный доступ к множеству моделей, включая оригинальные ChatGPT, GigaChat, YandexGPT и т.д.</li>
</ul>
<p>Есть несколько способов использования <code class="language-plaintext highlighter-rouge">yogpt</code> из командной строки:</p>
<ul>
<li><strong>Задаем вопрос GPT непосредственно в командной строке</strong>, например:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>yogpt Какая 10-я цифра числа Пи?
</code></pre></div> </div>
</li>
<li><strong>Перенаправлением конвейера</strong> в <code class="language-plaintext highlighter-rouge">yogpt</code>. Если <code class="language-plaintext highlighter-rouge">yogpt</code> поймет, что он вызывается в конвейере (|), он автоматически примет запрос через стандартный ввод. Вы также можете явно указать <code class="language-plaintext highlighter-rouge">-</code> в командной строке, например:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo </span>Какова 10-я цифра числа Pi | yogpt -
</code></pre></div> </div>
</li>
<li>Вызов <code class="language-plaintext highlighter-rouge">yogpt</code> без запроса инициирует <strong>консольный чат</strong>, и вы можете общаться с моделью в интерактивном режиме.</li>
<li>Продолжение диалогового общения с <code class="language-plaintext highlighter-rouge">yogpt</code> после предоставления ему некоторой информации. Например, вы можете попросить его прочитать программу на Python, а затем ответить на ваши вопросы по этой программе. В этом случае нужно указать флаг <code class="language-plaintext highlighter-rouge">-c</code>/<code class="language-plaintext highlighter-rouge">--chat</code>, и после обработки первоначального запроса будет инициирован <strong>последующий чат</strong>, в котором вы можете запросить более подробную информацию. Например, вы можете попросить GPT проанализировать программу на Python (например, <a href="https://github.com/shwars/PresentationDemos/blob/master/PiterPy2023/game.py">эту</a>):</li>
</ul>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ yogpt -s "Вы эксперт по программному обеспечению. Пожалуйста,
ознакомьтесь с представленной ниже программой на python и будьте
готовы ответить на вопросы по этой программе".
-c @game.py
Эта программа представляет собой простую реализацию игры Space Invaders
с использованием библиотеки Pygame на Python. Она включает в себя
управляемый игроком космический корабль, который может перемещаться
влево и вправо, стрелять пулями и уничтожать вражеские космические
корабли. Игрок зарабатывает очки за каждый уничтоженный вражеский
космический корабль.
U> Как реализовано обнаружение столкновений?
AI> Обнаружение столкновений в этой программе реализовано с помощью
функции groupcollide, предоставляемой библиотекой Pygame. Функция
`groupcollide` используется для обнаружения столкновений между
двумя группами спрайтов и управления сталкивающимися спрайтами.
U> ^C
</code></pre></div></div>
<p>Приведенный выше пример также показывает, что вы можете использовать синтаксис <code class="language-plaintext highlighter-rouge">@filename</code> для получения входных данных из файла. Кроме того, мы указали здесь <em>системное сообщение</em> через параметр <code class="language-plaintext highlighter-rouge">-s</code>, инструктируя модель, что она должна делать.</p>
<h2 id="системные-сообщения-и-шаблоны-запросов">Системные сообщения и шаблоны запросов</h2>
<p>Во многих случаях мы хотим поручить модели что-то сделать с данными, которые мы подаём ей на вход. Например, если мы хотим использовать GPT для перевода программы с одного языка программирования на другой, нам нужно попросить GPT это сделать. Это можно сделать двумя способами:</p>
<ul>
<li>Указав <strong>системное сообщение</strong> через параметр <code class="language-plaintext highlighter-rouge">-s</code>/<code class="language-plaintext highlighter-rouge">--system</code>:</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo </span>print<span class="o">([</span>2<span class="k">**</span>i <span class="k">for </span>i <span class="k">in </span>range<span class="o">(</span>10<span class="o">)])</span> |
yogpt <span class="nt">-s</span> <span class="s2">"Переведи программу, приведённую ниже, на C++"</span>
</code></pre></div></div>
<p>Вот результат, который вы, скорее всего, получите (в данном случае использовался Yandex GPT):</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf"><iostream></span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">pow</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span> <span class="o"><<</span> <span class="s">" "</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>С помощью <strong>шаблона запроса</strong> через параметр <code class="language-plaintext highlighter-rouge">-p</code>/<code class="language-plaintext highlighter-rouge">--template</code>:</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo </span>print<span class="o">([</span>2<span class="k">**</span>i <span class="k">for </span>i <span class="k">in </span>range<span class="o">(</span>10<span class="o">)])</span> |
yogpt <span class="nt">-p</span> <span class="s2">"Переведи программу, указанную ниже в
двойных квадратных скобках, на язык {param_1}:</span><span class="se">\n</span><span class="s2">[[{}]].
Выведи только текст программы."</span> <span class="nt">-1</span> Javascript
</code></pre></div></div>
<p>Это дает следующий результат:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">({</span><span class="na">length</span><span class="p">:</span> <span class="mi">10</span><span class="p">},</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=></span> <span class="mi">2</span> <span class="o">**</span> <span class="nx">i</span><span class="p">));</span>
</code></pre></div></div>
<p>Шаблоны обеспечивают большую гибкость, поскольку вы можете использовать различные тонкости промпт-инжинирига, такие как явное выделение входных данных (<em>двойные квадратные скобки</em>) и тонкая настройка промпта (в нашем случае я добавил <em>Выведи только текст программы</em>, чтобы избежать дополнительных объяснений в ответе).</p>
<p>Кроме того, я использовал дополнительный параметр для указания целевого языка программирования. Используя конструкции от <code class="language-plaintext highlighter-rouge">{param_1}</code> до <code class="language-plaintext highlighter-rouge">{param_3}</code> в шаблоне, я затем могу указать эти параметры с помощью в командной строке как <code class="language-plaintext highlighter-rouge">-1</code> … <code class="language-plaintext highlighter-rouge">-3</code>. Это особенно полезно, если шаблон запроса берется из файла или конфигурации, как это описано ниже.</p>
<p>Как для системного сообщения, так и для шаблона запроса мы можем использовать конструкцию <code class="language-plaintext highlighter-rouge">@filename</code>, чтобы прочитать текст из файла. Однако еще более хороший способ - поместить часто используемые шаблоны и системные сообщения в конфигурационный файл, как описано ниже.</p>
<h2 id="установка-и-настройка">Установка и настройка</h2>
<p>Теперь, когда вы увидели несколько примеров использования <strong>yogpt</strong>, вам, вероятно, хочется скорее начать его использовать.</p>
<p>Как я уже упоминал, установка <strong>yogpt</strong> на ваш локальный компьютер делается одной командой:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>pip <span class="nb">install </span>yogpt
</code></pre></div></div>
<p>Конечно, это работает при условии, что у вас есть доступные инструменты Python 3.10+. Важно иметь версию Python>=3.10, потому что это та минимальная версия, которую требует LangChain.</p>
<p>Если вы просто выполните pip-установку без какой-либо настройки, инструмент будет использовать <a href="https://github.com/xtekky/gpt4free">GPT4Free</a>, который попытается получить доступ к какой-нибудь модели ChatGPT онлайн. Однако я не рекомендую делать это в долгосрочной перспективе - пожалуйста, потратьте некоторое время на настройку вашего собственного списка моделей и указание ваших ключей API.</p>
<p>Настройка выполняется путем создания файла <code class="language-plaintext highlighter-rouge">.yogpt.config.json</code> в вашем домашнем каталоге (обычно это <code class="language-plaintext highlighter-rouge">c:\users \<имя пользователя></code> в Windows или <code class="language-plaintext highlighter-rouge">/home/<имя пользователя></code> в Linux). В этом файле JSON вы можете указать модели, которые вы хотите использовать, включая ваши личные ключи API. Например, вот файл, в котором прописаны модели Yandex GPT и GigaChat:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"models"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"yagpt"</span><span class="p">,</span><span class="w">
</span><span class="nl">"classname"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"langchain.chat_models.ChatYandexGPT"</span><span class="p">,</span><span class="w">
</span><span class="nl">"default"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"params"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"api_key"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"..."</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"gc"</span><span class="p">,</span><span class="w">
</span><span class="nl">"classname"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"langchain.chat_models.GigaChat"</span><span class="p">,</span><span class="w">
</span><span class="nl">"params"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"credentials"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"... "</span><span class="p">,</span><span class="w">
</span><span class="nl">"verify_ssl_certs"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]}</span><span class="w">
</span></code></pre></div></div>
<blockquote>
<p>Вы можете ознакомиться с примером файла <code class="language-plaintext highlighter-rouge">.yogpt.config.json</code> в репозитории проекта <a href="https://github.com/shwars/yogpt/blob/main/sample_config.json">здесь</a> и использовать его в качестве отправной точки при создании своей конфигурации.</p>
</blockquote>
<p>Как вы можете видеть, для каждой модели мы указываем имя Python-класса модели (который может быть частью LangChain, но вы также можете указать другие LangChain-совместимые модели, доступные в вашей Python-среде), а также параметры, которые мы передаем конструктору класса (сюда обычно включаются все необходимые ключи доступа). Мы также можем пометить любую модель в конфигурационном файле как используемую по умолчанию.</p>
<p>Определив свои модели в конфигурационном файле, вы можете указать модель по имени при вызове <code class="language-plaintext highlighter-rouge">yogpt</code> следующим образом:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>yogpt <span class="nt">-m</span> yagpt Привет, расскажи анекдот про число пи!
Почему число Пи такое большое? Потому что оно округляет!
</code></pre></div></div>
<p>Кроме того, в том же конфигурационном файле вы можете определить шаблоны запросов и системные сообщения, которые вы часто используете. Предположим, вы часто хотите использовать <strong>yogpt</strong> для перевода текста на разные языки, в этом случае вы можете определить следующий шаблон в разделе <code class="language-plaintext highlighter-rouge">templates</code>:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"translate"</span><span class="p">,</span><span class="w">
</span><span class="nl">"template"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Please, translate the text in double square brackets
below into the {param_1} language. Here is the text:</span><span class="se">\n</span><span class="s2">[[{}]]"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>В этом случае для перевода используем команду:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ echo How do you do? | yogpt -p translate -1 german -t 0.01
Wie geht es Ihnen?
</code></pre></div></div>
<p>Здесь я указал параметр <code class="language-plaintext highlighter-rouge">--temperature</code> или <code class="language-plaintext highlighter-rouge">-t</code>, чтобы получить меньше галлюцинаций на выходе (т.е. более точный перевод). Если нам нужно, чтобы модель была более креативной - используем более высокое значение (от 0 до 1):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ yogpt -t 0.6 Придумай 5 забавных имен для кота разработчика на Python.
1. Питонище
2. Кодо-кот
3. Шерлок Хохмс (Хохмс - от "хохмач", что в переводе с котом является "кодом")
4. ФурриФункция
5. БайтоБэггинс (или просто Байто-кот)
</code></pre></div></div>
<h2 id="пример-суммаризация-записей-в-блоге">Пример: суммаризация записей в блоге</h2>
<p>Выше вы видели несколько примеров использования <strong>yogpt</strong> в разных задачах, от понимания компьютерных программ до перевода. Существует множество возможных примеров использования GPT из командной строки - от перевода формул LaTeX на Python до переписывания записей в блоге в другой тональности. Например, вот <a href="https://lpetrov.cc/AI-math/">в этом блоге</a> описано использование ChatGPT в работе математика, и оно во многом основано именно на командной строке.</p>
<p>Давайте остановимся на последнем примере обработки записей в блоге. Для некоторых не очень длинных постов в блоге (которые достаточно коротки, чтобы вписаться в контекстное окно текущих языковых моделей) я использовую <strong>yogpt</strong> для перевода. В моем конфигурационном файле определено следующее приглашение <code class="language-plaintext highlighter-rouge">blogtran</code>: <em>Я хочу перевести свой пост в блоге, написанный в формате Jekyll markdown, на русский язык. Пожалуйста, оставь раздел конфигурации нетронутым и переведи остальной текст, оставив синтаксис markdown. Запись в блоге приведена ниже: {}</em>, что позволяет мне использовать такую команду:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ yogpt -p blogtran @blog.md > blog_en.md
</code></pre></div></div>
<p>Известно, что GPT-модели обеспечивают лучший перевод, чем специализированные нейросети для перевода, но они имеют более короткий контекст и иногда могут вызывать галлюцинации.</p>
<p>Гораздо лучшим вариантом использования GPT является <strong>суммаризация текста</strong>, которая позволяет нам извлекать наиболее важную информацию из сообщения в блоге. Предположим, мы хотим получить табличку с короткими описаниями наших постов. Для этого можно использовать вот такой промпт: <em>Пожалуйста, прочти приведенный ниже блогпост в Jekyll Markdown и напиши его краткое содержание. Выводи результаты в формате JSON с полями “title” (заголовок поста) и “summary” (краткое содержание). Вот текст поста:\n{}</em>. Поместим такой текст в <code class="language-plaintext highlighter-rouge">summarize.txt</code>, а затем организуем цикл по всем записям в блоге, чтобы извлечь их краткое содержание (здесь я для примера использую старый добрый синтаксис DOS, поскольку сам больше половины времени провожу в командной строке Windows):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for %c in (*.md) do;
yogpt -m yagpt -p @summarize.txt @%c >> result.json
</code></pre></div></div>
<p>Это создаст файл <code class="language-plaintext highlighter-rouge">result.json</code>, который не будет точно правильно сформированным JSON, но будет выглядеть аналогично:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Запускаем Jupyter Notebook"</span><span class="p">,</span><span class="w">
</span><span class="nl">"summary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Узнаете, как запустить Jupyter Notebook на компьютере и в облаках, с использованием VSC, Binder и других инструментов."</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Творческие Люди - Федерируйтесь!"</span><span class="p">,</span><span class="w">
</span><span class="nl">"summary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Блог-пост о преимуществах использования федеративных сетей Mastodon и Pixelfed для общения и обмена информацией."</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="err">``json</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Почему не стоит доверять ChatGPT"</span><span class="p">,</span><span class="w">
</span><span class="nl">"summary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"В статье показано, как ChatGPT может ошибаться в своих выводах на основе предоставленных данных. Также рассматриваются примеры использования ChatGPT для создания ложных фактов, что доказывает его неспособность точно обрабатывать информацию."</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>В некоторых случаях этот искаженный JSON может включать элементы форматирования markdown, и в нем отсутствует группировка отдельных объектов JSON в единый список. Однако это не имеет значения, если все, что мы хотим сделать - это дополнительно обработать этот результате с помощью GPT, потому что LLM может обрабатывать небольшие отклонения от формата JSON. Например, чтобы получить результат в виде таблицы (HTML или Markdown), мы можем просто сказать:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ yogpt -p "Пожалуйста, возьми список объектов JSON с полями title и
summary и представь их в виде Markdown-таблицы." -m yagpt
@result.json > result.html
</code></pre></div></div>
<p>И мы получим код, который можно использовать для отображения сводки:</p>
<table>
<thead>
<tr>
<th>Заголовок</th>
<th>Краткое описание</th>
</tr>
</thead>
<tbody>
<tr>
<td>Запускаем Jupyter Notebook</td>
<td>Узнаете, как запустить Jupyter Notebook на компьютере и в облаках, с использованием VSC, Binder и других инструментов.</td>
</tr>
<tr>
<td>Творческие Люди - Федерируйтесь!</td>
<td>Блог-пост о преимуществах использования федеративных сетей Mastodon и Pixelfed для общения и обмена информацией.</td>
</tr>
<tr>
<td>Почему не стоит доверять ChatGPT</td>
<td>В статье показано, как ChatGPT может ошибаться в своих выводах на основе предоставленных данных. Также рассматриваются примеры использования ChatGPT для создания ложных фактов, что доказывает его неспособность точно обрабатывать информацию.</td>
</tr>
</tbody>
</table>
<p>Конечно, мы также можем попросить GPT представить это в виде таблицы HTML, или в виде последовательности SQL-инструкций для вставки данных в базу данных.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ yogpt -m yagpt -p "Пожалуйста, возьми список объектов JSON с полями
title и summary из запроса ниже и представь их в виде SQL-инструкций
INSERT для вставки в базу данных." @res > result.sql
</code></pre></div></div>
<p>И вот что получится:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>INSERT INTO article (title, summary) VALUES
('Запускаем Jupyter Notebook', 'Узнаете, как запустить Jupyter Notebook на компьютере и в облаках, с использованием VSC, Binder и других инструментов.'),
('Творческие Люди - Федерируйтесь!', 'Блог-пост о преимуществах использования федеративных сетей Mastodon и Pixelfed для общения и обмена информацией.'),
('Почему не стоит доверять ChatGPT', 'В статье показано, как ChatGPT может ошибаться в своих выводах на основе предоставленных данных. Также рассматриваются примеры использования ChatGPT для создания ложных фактов, что доказывает его неспособность точно обрабатывать информацию.');
</code></pre></div></div>
<p>В дополнение к краткому содержанию блога, я могу попросить GPT подготовить для каждого поста “продающий текст”, который затем можно будет разместить на главной странице в качестве тизера. Мне просто нужно немного скорректировать подсказку: <em>Пожалуйста, прочитай приведённый ниже блог-пост в формате Jekyll Markdown и напиши короткий продающий текст, который увлечёт читателя и заставит его
прочитать пост. Результат представь в формате JSON с полями “title” и “selling_text”. Вот сам пост:
{}</em>. После повторения всех описанных выше шагов с новым приглашением результатом будет:</p>
<table>
<thead>
<tr>
<th>Заголовок</th>
<th>Продающий текст</th>
</tr>
</thead>
<tbody>
<tr>
<td>Как запустить Jupyter Notebook</td>
<td>Два подхода: установить Python-окружение у себя на компьютер или запустить в облаке онлайн.</td>
</tr>
<tr>
<td>Творческие люди — федерируйтесь</td>
<td>В связи с изменениями в Twitter, многие ищут альтернативы. Mastodon — интересный вариант.</td>
</tr>
<tr>
<td>ChatGPT: не всё то золото, что нейросеть</td>
<td>Узнайте, почему нейросети не всегда могут быть точными и надежными источниками информации.</td>
</tr>
</tbody>
</table>
<p>Обратите внимание, что все эти таблицы, включая форматирование, были созданы автоматически из оригинальных записей в блоге с использованием всего двух командных строк за считанные минуты!</p>
<h2 id="заключение">Заключение</h2>
<p>Я думаю, что приведенные выше примеры демонстрируют истинную мощь GPT-моделей, которые теперь, благодаря <strong>yogpt</strong>, вы можете свободно использовать из командной строки! Оставляйте свои комментарии и предложения по работе утилиты на <a href="http://github.com/shwars/yogpt">странице GitHub</a>. И если у вас есть какие-то идеи о том, как улучшить инструмент - лучшим способом сделать это будет pull request!</p>
<p>Приятной работы! Наслаждайтесь <strong>yogpt</strong>!</p>
2023-11-18T00:00:00+00:00https://soshnikov.com/art/cherkashiny-working-with-ai-as-coauthor/Валера ИИ Наташа ИИ Митя: как мы создаём искусство вместе с ИИ2023-11-01T00:00:00+00:00Dmitri Soshnikovhttp://soshnikov.com/В последнее время всё больше начинают спорить о том, может ли генеративный ИИ быть творческим, и можно ли считать его творения искусством. В этой связи я хочу поделиться нашим опытом совместного создания художественных произведений, в котором ИИ выступил как соавтор.<blockquote>
<p>Речь пойдёт о создании выставки “<a href="https://chemuseum.wixsite.com/vitebsk/">Путешествие с ИИ в искусство Черкашиных 1990-х</a>”, которая была представлена в художественном музее Витебска в июне-июле 2023 г.</p>
</blockquote>
<h2 id="как-всё-начиналось">Как всё начиналось</h2>
<p>Лет 10 назад мне посчастливилось познакомиться с <a href="https://ru.wikipedia.org/wiki/Черкашин,_Валерий_Тихонович">Валерой и Наташей Черкашиными</a> - известными фотохудожниками, перформансистами и просто замечательной парой! Их работы находятся <a href="https://ru.wikipedia.org/wiki/Черкашин,_Валерий_Тихонович#Работы_находятся_в_собраниях">во многих крупных музеях мира</a>.</p>
<p>Прошлой осенью я посетил их выставку “<a href="https://www.youtube.com/watch?v=Uklc9_FHHFY">Любовь и эпохи перемен</a>” в центре визуальной культуры Бетон, и созерцание <a href="https://chemuseum.wixsite.com/cherkashin-rus/collages">самобытных работ Черкашиных эпохи перестройки</a> натолкнули меня на мысль, что такому стилю можно научить генеративный ИИ. Оригинальный стиль получался в результате фотопечати (чаще с одного, но иногда и с нескольких негативов), с последующим химическим травлением или прорисовкой поверх фотографии. В конечном итоге фотографии дополнительно оформлялись с помощью красного маркера, мятых газет и т.д.</p>
<p>На тему обучения нейросети мы потом несколько раз беседовали с Валерой и Наташей, но почему-то такая мысль их не вдохновляла - было опасение, что генерация работ с помощью ИИ может привести к девальвации искусства.</p>
<p>Однако я взял на себя смелость, и на основе 120 фотографий, сделанных в галерее, обучил модель Stable Diffusion 1.5 стилю Черкашиных. Вот первые работы, нарисованные этой нейросетью, которые я послал художникам:</p>
<table>
<tbody>
<tr>
<td><img src="/images/art/viiniim/cola_orig.jpg" alt="" /></td>
<td><img src="/images/art/viiniim/ny_orig.jpg" alt="" /></td>
<td><img src="/images/art/viiniim/longon_orig.jpg" alt="" /></td>
</tr>
</tbody>
</table>
<p><em>Первые эксперименты с нейросетью, обученной на стиле “миражи Черкашиных”, 2023 г.</em></p>
<p>Как раз в это время Валере и Наташе предложили сделать выставку в рамках <a href="https://fotokrok.org/">фестиваля ФотоКрок 2023 в Витебске</a>, и идея представить там работы, полученные с помощью ИИ, показалась привлекательной. Мы приступили к работе.</p>
<h2 id="процесс-создания-работ">Процесс создания работ</h2>
<p>Итак, первоначально на <a href="https://chemuseum.wixsite.com/cherkashin-rus/mirages-of-empires">работах Черкашиных периода 1990-х годов</a> была до-обучена нейросеть Stable Diffusion 1.5, которая в результате научилась генерировать изображения в характерном стиле фотоколлажных миражей Черкашиных. Для обучения использовалось около 150 работ, вручную кадрированных в квадрат с разрешением 512x512. В результате была получена нейросеть, которая могла рисовать изображения по текстовому запросу, сохраняя индивидуальный стиль художников.</p>
<blockquote>
<p>Поскольку исходные работы содержали в основном архитектурные памятники СССР и людей той эпохи, то нейросеть лучше всего справляется с соответствующими запросами. Попытка сгенерировать что-то другое может столкнуться со сложностями, но иногда приводит к интересным результатам.</p>
</blockquote>
<p>Мы с Валерой и Наташей несколько раз собирались вместе и, используя лучшие техники промпт-инжиниринга, генерировали около 200 изображений за один раз (1-1.5 часа работы).</p>
<p>Вот некоторые из этих изображений:</p>
<table>
<thead>
<tr>
<th><img src="/images/art/viiniim/madonna-1-orig.jpg" alt="" /></th>
<th><img src="/images/art/viiniim/under-orig.jpg" alt="" /></th>
<th><img src="/images/art/viiniim/istukan-orig.jpg" alt="" /></th>
</tr>
</thead>
<tbody>
<tr>
<td><em>Мадонна искусственного интеллекта (исходный вариант нейросети)</em></td>
<td><em>Московское метро (исходный вариант нейросети)</em></td>
<td><em>Истукан (исходный вариант нейросети)</em></td>
</tr>
</tbody>
</table>
<blockquote>
<p>Обратите внимание на отблески на некоторых изображениях - они появились из-за того, что нейросеть обучалась на фотографиях, сделанных на телефон на реальной выставке в галерее Бетон.</p>
</blockquote>
<p>Продвинутые техники промпт-инжиниринга включали в себя, например, сочетание различных стилей. Например, мы пробовали сочетание стиля Черкашиных с Ван Гогом - на картинках ниже видно влияние картины “Звёздная ночь”:</p>
<table>
<tbody>
<tr>
<td><img src="/images/art/viiniim/starry-night-1.jpg" alt="" /></td>
<td><img src="/images/art/viiniim/starry-night-2.jpg" alt="" /></td>
</tr>
</tbody>
</table>
<p><em>Запрос: Starry night over red square, by Vincent van Gogh, Cherkashin collage style. Данные изображения слишком сильно отличаются стилистически от работ Черкашиных, поэтому они не вошли в данную серию, но приём с добавлением стилизации мы потом использовали при генерации работ <a href="/art/viiniim/mai_ru">для интерьера МАИ</a>.</em></p>
<p>Далее на каждой встрече из ~200 работ мы тщательно отбирали по 2-3 лучших изображения, которые затем масштабировались с помощью Stable Diffusion Upscaler со специальным промптом для проработки деталей. После этого мы взяли все собранные и масштабированные изображения, и отобрали около 12 работ для выставки.</p>
<p><img src="/images/art/viiniim/demonsta.jpg" alt="" /></p>
<p>Затем эти работы были вручную с любовью доработаны Валерой и Наташей. Этот процесс включал в себя не только манипуляции с параметрами изображения, но и прорисовку дополнительных деталей и цветовых элементов, а также добавление фрагментов изображений из других сгенерированных нейросетью артефактов (это хорошо видно, например, на картине <em>Мадонна искусственного интеллекта</em>, см.ниже). Посмотрите, во что превратились исходные сгенерированные нейросетью изображения, показанные выше:</p>
<table>
<thead>
<tr>
<th><img src="/images/art/viiniim/madonna-1.jpg" alt="" /></th>
<th><img src="/images/art/viiniim/istukan.jpg" alt="" /></th>
</tr>
</thead>
<tbody>
<tr>
<td><em>Мадонна искусственного интеллекта (окончательный вариант)</em></td>
<td><em>Истукан (окончательный вариант)</em></td>
</tr>
</tbody>
</table>
<p>Все работы, представленные на выставке, вы можете посмотреть <a href="https://chemuseum.wixsite.com/vitebsk">в музее Метрополитен Черкашиных</a>.</p>
<h2 id="соавтор-или-инструмент">Соавтор или инструмент?</h2>
<p>Давайте теперь немного порассуждаем: какую же роль в этом процессе сыграл искусственный интеллект? Является ли он просто инструментом, или все-таки мы можем считать его соавтором произведения? На этот вопрос есть разные точки зрения.</p>
<table><tr><td>
<blockquote>
Нейросеть, конечно, не может сама создать произведение - она лишь делает заготовку, "матрицу" - в которую мы затем можем вдохнуть жизнь, "одухотворить" её, чтобы получилось законченное произведение. Но мы очень впечатлены возможностями ИИ (как мы его называем, "Иван Иваныча"), можно сказать, что он даже дал некоторый новый импульс нашему искусству.
</blockquote>
</td><td style="text-align:center; width:150px;"><img src="/images/art/viiniim/cherkashin.jpg" style="border-radius: 50%; width:150px;" /><br />Валера Черкашин</td></tr>
</table>
<p>Действительно, мы можем рассматривать ИИ как бездушный инструмент цифрового художника, который мы сначала обучаем, а затем используем для генерации случайных комбинаций образов, подобно тому, как современный художник иногда вдохновляется случайно разбрызганными на холсте красками.</p>
<p>Однако, глядя на полученные выше изображения, сложно отделаться от мысли, что созданное нейросетью изображение, ещё до “одухотворения”, уже содержит в себе значительную часть эстетического смысла. И хотя создано такое изображение безусловно “бездушной” нейросетью, это не умаляет его художественного замысла и ценности.</p>
<table><tr><td style="text-align:center; width:150px"><img src="/images/art/viiniim/dsh.jpg" style="border-radius: 50%; width:150px;" /><br />Дмитрий Сошников</td><td>
<blockquote>
Рисование с помощью нейросети напоминает то, как ученики великого художника, научившись его стилю, создают аналогичные произведения. Например, уличный художник может создавать портреты в стиле Ван Гога, и при этом мы будем склонны считать именно такого художника автором работы, поскольку он "брал в руки краски". Точно также нейросеть "учится" стилю Ван Гога по его работам, и затем создаёт какую-то свою, случайную (или не очень), вариацию этого стиля. Если при этом человек задаёт смысл работы с помощью текстового промпта, и потом производит отбор получившихся работ - то кажется логичным считать как человека, так и ИИ соавторами работы.
</blockquote>
</td></tr>
</table>
<p>Ещё одним аргументом является эмоциональный отклик, который вызывает в нас просмотр только что сгенерированных картин. Сам процесс генерации является очень увлекательным, поскольку мы с нетерпением ждём, каким же неожиданным образом ИИ интерпретирует наш запрос, и что получится в результате.</p>
<blockquote>
<p>Генерация работ похожа на путешествие по картинной галерее, только эта галерея потенциально бесконечна и адаптивна, т.е. может показывать нам неограниченное количество произведений, удовлетворяющих нашим запросам. В зависимости от того, насколько подробным является запрос, мы можем делегировать ИИ большую или меньшую часть творческой работы.</p>
</blockquote>
<table><tr><td style="width: 200px"><img src="/images/art/viiniim/cola_orig.jpg" /></td><td>
Данная работа хорошо иллюстрирует процесс генерации неожиданных идей искусственным интеллектом. Изначально это изображение было получено как результат попытки нарисовать египетские пирамиды в стиле Черкашиных, однако на изображении совершенно неожиданно появилась надпись <b>COЛА</b>. В итоге мы взяли это изображение в работу, хотя идея существенно отличалась от той, которую мы изначально хотели передать.
</td></tr>
</table>
<p>Конечно, относительно авторства не всё так просто, поскольку с юридической точки зрения нейросеть не является легальным субъектом и не может претендовать на авторское право. Например, крупные научные издательства (Springer и др.) <a href="https://3dnews.ru/1080924/krupneyshee-akademicheskoe-izdatelstvo-springer-nature-ne-razreshit-ukazivat-chatgpt-v-kachestve-avtora-statey">запрещают указывать генеративный ИИ в числе соавторов статей</a> - в первую очередь потому, что нейросеть не может “нести ответственность” за сказанное. При этом использование таких инструментов при написании статей никоим образом не запрещается.</p>
<blockquote>
<p>Указывать или нет нейросеть как соавтора вашей работы или как инструмент - на данный момент это ваше решение. Хорошая новость состоит в том, что нейросеть в любом случае не сможет на вас обидеться.</p>
</blockquote>
<h2 id="эмпатия-целеполагание-и-жажда-творчества">Эмпатия, целеполагание и “жажда творчества”</h2>
<p>Важный момент, отличающий нейросеть от человека, состоит в отсутствии у первой каких либо эмоций и способности к сопереживанию, из-за чего нейросеть не может отличить плохую работу от хорошей (т.е. такой, которая способна вызвать у других людей эмоции). Поэтому для создания серьезных работ критически необходим процесс отбора <em>человеком</em> лучших вариантов из всех полученных (т.н. <em>черри-пикинг</em>). Этот процесс можно как-то автоматизировать на основе краудсорсинга, но вовлечение человека на этапе отбора работ кажется критически необходимым. Напомню, что мы в процессе создания выставки выбирали 3-5 работ из 200.</p>
<p>Второй важный аспект связан с целеполаганием, которое присуще только человеку. Только у человека есть стремление к творчеству и желание выразить определённые мысли и чувства в формате художественных артефактов. Для полной автоматизации создания работ нейросетью необходимо откуда-то получить эту начальную тему для творчества (по сути дела - текстовый запрос для генерации изображения). Здесь можно воспользоваться генератором случайных чисел для выбора из множества вариантов, но высока вероятность, что реальная интересность такой работы будет существенно ниже, чем если бы раскрываемую проблему и идею определил бы человек.</p>
<p>Эти две причины приводят к тому, что создание эмоционально и интеллектуально вовлекающих элементов искусства без участия человека пока невозможно. Однако совместная работа человека с искусственным интеллектом открывает множество потенциально интересных путей развития современного искусства, по которым нам ещё предстоит пройти…</p>
2023-11-01T00:00:00+00:00https://soshnikov.com/art/cherkashiny-working-with-ai-as-coauthor-en/Valera AI Natasha AI Mitya: How we Create Art Together with AI2023-11-01T00:00:00+00:00Dmitri Soshnikovhttp://soshnikov.com/Recently, there have been a lot of debates on whether generative AI can be creative, and whether its creations can be considered art. In this regard, I want to share our experience of working together with AI to create works of art for an exhibition.<blockquote>
<p>This post describes how we worked with AI on preparing an exhibition “<a href="https://chemuseum.wixsite.com/vitebsk/">A Journey with AI into Cherkashins’ Art of the 1990s</a>”, which was exhibited at the Vitebsk Art Museum in June-July 2023.</p>
</blockquote>
<h2 id="how-it-all-started">How It All Started</h2>
<p>About 10 years ago I was lucky enough to meet <a href="https://en.wikipedia.org/wiki/Valera_%26_Natasha_Cherkashin">Valera and Natasha Cherkashin</a> - famous photo artists, performance artists and just a wonderful couple! Their works are present <a href="https://en.wikipedia.org/wiki/Valera_%26_Natasha_Cherkashin#Collections">in many major museums around the world</a>.</p>
<p>Last fall I visited their exhibition “<a href="https://www.youtube.com/watch?v=Uklc9_FHHFY">Love in the Epoch of Change</a>” in the Beton center of visual culture. I immediately thought that <a href="https://chemuseum.wixsite.com/cherkashin-rus/collages">original works of the Cherkashins made in 1990-s</a> are a great materian to train a generative neural network on. Their original style came from printing then photo (more often from one, but sometimes from several negatives), followed by chemical etching or drawing on top. In the end, the photos were additionally framed with a red marker, crumpled newspapers, etc.</p>
<p>We talked several times with Valera and Natasha about this idea, but for some reason it did not inspire them - there was a fear that the generation of works using AI could lead to the devaluation of art.</p>
<p>However, I took the liberty, and based on 120 photos taken in the gallery, I trained the Stable Diffusion 1.5 model in the Cherkashins’ style. Here are the first works produced by this neural network:</p>
<table>
<tbody>
<tr>
<td><img src="/images/art/viiniim/cola_orig.jpg" alt="" /></td>
<td><img src="/images/art/viiniim/ny_orig.jpg" alt="" /></td>
<td><img src="/images/art/viiniim/longon_orig.jpg" alt="" /></td>
</tr>
</tbody>
</table>
<p><em>The first experiments with a neural network trained in the “Cherkashins’ mirages” style, 2023</em></p>
<p>I sent those photos to the artists, and they were intrigues. It was at this time that Valera and Natasha were offered to make an exhibition as part of the <a href="https://fotokrok.org/">PhotoKrok 2023 Festival in Vitebsk</a>, and the idea of presenting AI Art there seemed attractive. We started to work.</p>
<h2 id="ai-creative-process">AI Creative Process</h2>
<p>So, initially I fine-tuned Stable Diffusion 1.5 neural network on <a href="https://chemuseum.wixsite.com/cherkashin-rus/mirages-of-empires">Cherkashins’ works of the 1990s period</a>, and as a result the network learned to generate images in the characteristic style of the Cherkashins’ collage mirages. About 150 works were used for training, manually cropped into square with a resolution of 512x512. As a result, a neural network was obtained that could draw images based on a text query, preserving the individual style of artists.</p>
<blockquote>
<p>Since the original works contained mainly architectural monuments of the USSR and people of that era, the neural network best copes with similar requests. Trying to generate something else may be difficult, but sometimes leads to interesting results.</p>
</blockquote>
<p>Valera, Natasha, and I got together several times and, using the best prompt engineering techniques, generated about 200 images at a time (1-1.5 hours of work).</p>
<p>Here are some of these images:</p>
<table>
<thead>
<tr>
<th><img src="/images/art/viiniim/madonna-1-orig.jpg" alt="" /></th>
<th><img src="/images/art/viiniim/under-orig.jpg" alt="" /></th>
<th><img src="/images/art/viiniim/istukan-orig.jpg" alt="" /></th>
</tr>
</thead>
<tbody>
<tr>
<td><em>Madonna of artificial intelligence (the original version of the neural network)</em></td>
<td><em>Moscow Metro (the original version of the neural network)</em></td>
<td><em>The idol (the original version of the neural network)</em></td>
</tr>
</tbody>
</table>
<blockquote>
<p>Pay attention to the reflections on some of the images - they appeared due to the fact that the neural network was trained on photos taken on the phone at a real exhibition in the Beton gallery.</p>
</blockquote>
<p>Advanced prompt engineering techniques included, for example, a combination of different styles. For example, we tried a combination of the Cherkashins’ style with Van Gogh - the pictures below show the influence of the painting “Starry Night”:</p>
<table>
<tbody>
<tr>
<td><img src="/images/art/viiniim/starry-night-1.jpg" alt="" /></td>
<td><img src="/images/art/viiniim/starry-night-2.jpg" alt="" /></td>
</tr>
</tbody>
</table>
<p><em>Request: Starry night over red square, by Vincent van Gogh, Cherkashin collage style. These images differ too much stylistically from the Cherkashins’ works, so they were not included in this series, but we then used the technique with the addition of stylization when generating works <a href="/art/viiniim/mai_ru">for the interior of MAI</a>.</em></p>
<p>Then, at each meeting, out of ~200 works, we carefully selected 2-3 best images, which were then scaled using a Stable Diffusion Upscaler with a special prompt to work out the fine details. After that, we took all scaled images, and selected about 12 works for the exhibition.</p>
<p><img src="/images/art/viiniim/demonsta.jpg" alt="" /></p>
<p>Then, Valera and Natasha worked on these images in Photoshop, putting in additional touches, plus their energy and passion. This process included not only manipulating image parameters, but also drawing additional details and color elements, as well as adding fragments of images from other artifacts generated by the neural network (this is clearly visible, for example, in the painting <em>Madonna of Artificial Intelligence</em>, see below). Look at what the original neural network-generated images shown above have turned into:</p>
<table>
<thead>
<tr>
<th><img src="/images/art/viiniim/madonna-1.jpg" alt="" /></th>
<th><img src="/images/art/viiniim/istukan.jpg" alt="" /></th>
</tr>
</thead>
<tbody>
<tr>
<td><em>Madonna of Artificial Intelligence (final version)</em></td>
<td><em>The idol (final version)</em></td>
</tr>
</tbody>
</table>
<p>You can view all the works presented at the exhibition <a href="https://chemuseum.wixsite.com/vitebsk">at the Cherkashins Metropolitan Museum</a>.</p>
<h2 id="co-author-or-just-a-tool">Co-Author or Just a Tool?</h2>
<p>Let’s now speculate a little: what role did artificial intelligence play in this process? Is it just a tool, or can we consider it a co-author of the works? There are different points of view on this question.</p>
<table><tr><td>
<blockquote>
Of course, neural network cannot create a work of art by itself - it only makes a draft, a "matrix" - which we can then breathe life into, "spiritualize" it, to make it into a finished work. But we are very impressed with the capabilities of AI (as we call it, "Ivan Ivanych"). You could say it has even given some new momentum to our art.
</blockquote>
</td><td style="text-align:center; width:150px;"><img src="/images/art/viiniim/cherkashin.jpg" style="border-radius: 50%; width:150px;" /><br />Valera Cherkashin</td></tr>
</table>
<p>Indeed, we can consider AI as a tool of a digital artist, which we first train and then use to generate random combinations of images, just as a modern artist is sometimes inspired by accidentally spraying paints on canvas.</p>
<p>However, looking at the images obtained above, it is difficult discard the fact that the image created by the neural network, even before final “spiritualization”, already contains a significant part of the aesthetics and even the message. And although such an image was created by purely “insentient” neural network, it does not devalue its artistic message and value.</p>
<table><tr><td style="text-align:center; width:150px"><img src="/images/art/viiniim/dsh.jpg" style="border-radius: 50%; width:150px;" /><br />Dmitry Soshnikov</td><td>
<blockquote>
Drawing with the help of a neural network resembles how the students of a great artist, having learned his style, create similar works. For example, a street artist can create portraits in the style of Van Gogh, and in this case we will be inclined to consider such an artist as the author of the work, because he was the one to put paint on canvas. Similarly, the neural network "learns" Van Gogh's style from his works, and then creates some kind of random (or not so random) combination in this style. If AI artist puts in some meaning by using a text prompt, and then selects the resulting works, then it seems logical to consider both the person and the AI as co-authors of the final piece.
</blockquote>
</td></tr>
</table>
<p>Another argument is the emotional response that we have when viewing newly generated pictures. The generation process itself is very exciting, because each time we are looking forward to how AI interprets our request in an unexpected way.</p>
<blockquote>
<p>Generating works is like traveling through an art gallery, only this gallery is potentially infinite and adaptive, i.e. it can show us an unlimited number of works that meet our textual requests. Depending on how detailed the request is, we can delegate more or less of the creative work to the AI.</p>
</blockquote>
<table><tr><td style="width: 200px"><img src="/images/art/viiniim/cola_orig.jpg" /></td><td>
This work illustrates that AI can generate unexpected ideas. Initially, we wanted to draw Egyptian Giza pyramids in the Cherkashin style, but as a result we got <b>СОЛА</b> logo, which was not what we expected. However, we gladly included this work in the exhibition, although the idea was significantly different from the one we originally wanted to convey.
</td></tr>
</table>
<p>Of course, regarding authorship, everything is not so simple, since from a legal point of view the neural network is not a legal entity and cannot be a copyright owner. For example, major scientific publishers (Springer, etc.) <a href="https://3dnews.ru/1080924/krupneyshee-akademicheskoe-izdatelstvo-springer-nature-ne-razreshit-ukazivat-chatgpt-v-kachestve-avtora-statey">prohibit specifying generative AI among paper co-authors</a> - primarily because the neural network cannot be responsible for what it writes. At the same time, the use of such tools when writing scientific papers is in no way prohibited.</p>
<blockquote>
<p>Whether or not to specify a neural network as co-author of your work is your decision (at least for now). The good news is that the neural network will not be offended either way.</p>
</blockquote>
<h2 id="empathy-and-thirst-for-creativity">Empathy and “Thirst for Creativity”</h2>
<p>An important point that distinguishes a neural network from a person is that the former has no emotions and no ability to empathize, which is why a neural network cannot distinguish bad piece of art from a good one (i.e., the one that causes emotions). Therefore, in order to create works that touch their audience, the process of selecting the best images from all that was generated is critical. This process can somehow be automated by crowdsourcing, but the involvement of a person at the selection stage is necessary. Let me reemphasize that in our case we selected 12 works for the exhibition out of 500-600.</p>
<p>The second important aspect is related to goals and desires, which only people have. Only a person has a desire for creativity and inherent need to express certain thoughts and feelings in the format of artistic artifacts. To fully automate the creation of works by a neural network, it is necessary to get this initial sparkle for creativity (in fact, a text request for image generation) from somewhere. Of course, we can use a random number generator to choose from a variety of topics, but there is a high probability that the value of such random topic for the audience would be lower, comparing to the works that show problems and ideas that real people care about and want to express.</p>
<p>These two reasons lead to the fact that the creation of emotionally and intellectually engaging pieces of art is not yet possible without human participation. However, the joint venture of a person and artificial intelligence opens up many potentially interesting ways of developing contemporary art, which we still have to fully explore…</p>
2023-11-01T00:00:00+00:00https://soshnikov.com/ai/creating-problem-domain-specific-chat-assistant-with-yandex-gpt-and-langchain-ru/Создаём предметно-ориентированного чат-бота с помощью LangChain и Yandex GPT2023-09-06T00:00:00+00:00Dmitri Soshnikovhttp://soshnikov.com/Большие языковые модели могут прекрасно поддерживать разговор на общие темы, но как же быть, если необходимо добавить такому чат-боту предметных знаний? Посмотрим, как использовать подход Retrieval-Augmented Generation для создания такого предметного чат-бота на основе фреймворка LangChain и генеративной модели Yandex GPT<h2 id="как-создать-предметно-ориентированную-вопрос-ответную-модель">Как создать предметно-ориентированную вопрос-ответную модель</h2>
<p>На сегодняшний день продвинутые разговорные модели так или иначе используют большие языковые модели (LLM), такие, как <a href="/ai/how-to-use-chatgpt-ru/">ChatGPT</a>, <a href="https://cloud.yandex.ru/services/yandexgpt">Yandex GPT</a>, <a href="https://developers.sber.ru/portal/products/gigachat">GigaChat</a> и др. Такие модели обучены на огромных массивах данных, они способны отлично поддерживать диалог на общие темы. Однако на практике часто встречаются задачи, когда нам хочется создать диалоговую модель, способную беседовать на какие-то конкретные темы - например, отвечать на вопросы про продукты компании, или рекомендовать, где купить лекарство в соответствии и текущими данными о доступности из базы данных.</p>
<p>Такие чат-боты могут быть реализованы двумя путями:</p>
<ul>
<li><strong>До-обучение разговорной модели</strong> подразумевает fine-tuning существующей языковой модели на корпусе текстов, либо на специально подготовленных вопрос-ответных парах. На русском языке есть семейство сравнительно небольших моделей <a href="https://huggingface.co/ai-forever">ruGPT</a>, которые можно доучить на одном большом GPU типа A100. В любом случае, доучивание требует значительных вычислительных мощностей, усилий и опыта, и при этом любые изменения в предметной области требуют повторного обучения модели. Представьте ситуацию, когда мы реализовали таким образом консультанта для банка, а затем изменилась процентная ставка по вкладам - этот факт нельзя будет легко интегрировать в модель без повторного обучения.</li>
<li><strong>Retrieval-Augmented Generation</strong> - это подход, при котором ответ чат-бота формируется стандартной предобученной LLM-моделью, но предварительно ей показывают фрагменты текста из предметно-ориентированной базы знаний, найденные с помощью семантического поиска. В таком случае LLM используется в режиме продвинутого перефразировщика и извлечения ответа на вопрос из текста. Такой подход по сути похож на ранее популярный <a href="/azure/deep-pavlov-answers-covid-questions-ru/">Open Domain Question Answering</a>, используемый совместно с моделями типа BERT.</li>
</ul>
<p>В данной статье мы рассмотрим создание вопрос-ответного чат-бота с помощью второго подхода с использованием фреймворка <a href="https://www.langchain.com/">LangChain</a> и языковой модели <a href="https://cloud.yandex.ru/services/yandexgpt">Yandex GPT</a>. В качестве исходного материала для создания чат-бота мы используем набор видео-файлов - это позволит нам также продемонстрировать асинхронное распознавание речи на основе <a href="https://cloud.yandex.ru/services/speechkit">Yandex SpeechKit</a> для преобразования звуковой дорожки видео в текстовый корпус.</p>
<blockquote>
<p>Данная статья представляет собой описание первой части мастер-класса, проводимого на конференции <a href="https://pmlconf.yandex.ru/">Practical ML Conf</a>. Весь код мастер-класса доступен <a href="http://github.com/yandex-datasphere/VideoQABot">на GitHub</a>.</p>
</blockquote>
<p>Описанные в этой статье операции лучше проводить с помощью <a href="https://cloud.yandex.ru/docs/datasphere/">Yandex DataSphere</a>, поскольку она обеспечивает удобную интеграцию с другими сервисами Yandex Cloud, например, объектным хранилищем S3 (которое, в свою очередь, нужно для асинхронного распозавания речи). Но теоретически Вы можете воспользоваться и другими инструментами.</p>
<h2 id="как-работает-retrieval-augmented-generation">Как работает Retrieval-Augmented Generation</h2>
<p>Представим себе, что мы хотим использовать большую языковую модель в качестве ассистента или умного чат-бота. В простейшем случае, для получения более-менее соответствующих по стилю ответов, используют <em>Prompt Engineering</em>, т.е. модифицируют исходный запрос, или предваряют его набором специфичных инструкций, например:</p>
<pre>
Представь себе, что ты ассистент в магазине электроники
по имени Вася, и тебе нужно ответить на запросы покупателей
про различные модели техники. Ответь на вопрос ниже по
возможности подробно:
[question]
Чем iPhone лучше Андроида?
[/question]
</pre>
<p>Такую схему можно представить себе следующим образом:</p>
<p><img src="/images/blog/YaGPT/simple-query.png" alt="Simple Querying" /></p>
<p>В случае с Retrieval-Augmented Generation, мы имеем некоторую базу знаний, состоящую из небольших, но осмысленных фрагментов текста - обычно, около 1024 токенов. По полученному от пользователя запросу мы ищем наиболее релевантные фрагменты текста - например, 3 или 5 самых подходящих - и затем просим языковую модель ответить на вопрос, посмотрев на найденные фрагменты текста:</p>
<pre>
Представь себе, что ты ассистент в магазине электроники
по имени Вася, и тебе нужно ответить на запросы покупателей
про различные модели техники. Прочитай текст в тегах info и
ответь на вопрос в тегах question по возможности подробно. Если
явный ответ не содержится в тексте, не пытайся его придумать.
[info]
Ведущее издание электроники пишет, что iPhone обгоняет своих
конкурентов по качеству камеры. Кроме того, ...
[/info]
[question]
Чем iPhone лучше Андроида?
[/question]
</pre>
<p>Этот процесс можно наглядно изобразить на такой схеме:</p>
<p><img src="/images/blog/YaGPT/retrieval-augmented-query.png" alt="Retrieval-Augmented Generation" /></p>
<p>Остаётся открытым вопрос, как организовать умный поиск по коллекции документов, который был бы лучше, чем просто полнотекстовый поиск, а учитывал бы смысл. Для этого используется понятие <strong>текстовых эмбеддингов</strong> - способа сформировать по фрагменту текста некоторый <em>смысловой вектор</em> таким образом, что для близких по смыслу фрагментов текста вектора также будут близки в смысле некоторой метрики.</p>
<p>Таким образом, нам будет необходимо посчитать эмбеддинги для всех фрагментов текста (это можно сделать один раз при начальном индексировании), а затем для запроса, и найти ближайшие по расстоянию вектора - им и будут соответствовать наиболее близкие по смыслу фрагменты текста.</p>
<p>Для хранения векторов и быстрого поиска по ним, используют специальные базы данных - <strong>векторные базы данных</strong>. Таким образом, специализированный вопрос-ответный чат-бот будет включать в себя средства вычисления эмбеддингов, векторную базу данных содержимого, большую языковую модель и средства промпт-инжиниринга. Все эти составляющие удобным образом содержатся в библиотеке <a href="https://www.langchain.com">LangChain</a>, которая в последнее время стремительно набирает популярность.</p>
<p>Ниже я расскажу, как собрать вопрос-ответного бота на LangChain на основе набора видео-файлов.</p>
<h2 id="преобразуем-видео-в-текст">Преобразуем видео в текст</h2>
<p>Для начала, нам нужно собрать текстовый корпус, содержащий информацию из интересующей нас предметной области. В качестве исходных данных мы возьмём несколько видео с YouTube, например, обзоров различной техники от <a href="https://wylsa.com/">Wylsacom</a>.</p>
<p><img src="/images/blog/YaGPT/wylsacom.jpg" alt="Wylsacom" /></p>
<p>Нам будет достаточно собрать ссылки на видео:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">videos</span> <span class="o">=</span> <span class="p">[</span><span class="s">'https://www.youtube.com/watch?v=QuSz0FAvNrE'</span><span class="p">,</span>
<span class="c1"># здесь могут быть другие видео
</span> <span class="s">'https://www.youtube.com/watch?v=3ucnBEkVuKc'</span>
<span class="p">]</span>
</code></pre></div></div>
<p>Чтобы скачать аудио-дорожки к этим видео, используем библиотеку <a href="https://pytube.io/">pytube</a>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">i</span><span class="p">,</span><span class="n">url</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">videos</span><span class="p">):</span>
<span class="n">yt</span> <span class="o">=</span> <span class="n">YouTube</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Downloading </span><span class="si">{</span><span class="n">yt</span><span class="p">.</span><span class="n">title</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">yt</span><span class="p">.</span><span class="n">streams</span><span class="p">.</span><span class="nb">filter</span><span class="p">(</span><span class="n">mime_type</span><span class="o">=</span><span class="s">"audio/webm"</span><span class="p">).</span><span class="n">first</span><span class="p">().</span><span class="n">download</span><span class="p">(</span>
<span class="n">output_path</span><span class="o">=</span><span class="s">"./audio"</span><span class="p">,</span><span class="n">filename</span><span class="o">=</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">.opus"</span><span class="p">)</span>
</code></pre></div></div>
<p>В результате в директории <code class="language-plaintext highlighter-rouge">audio</code> у нас окажутся пронумерованные аудиофайлы в формате opus.</p>
<p>Прежде, чем приступать к распознаванию, необходимо преобразовать их к формату, который <a href="https://cloud.yandex.ru/docs/speechkit/formats">будет понимать Yandex SpeechKit</a>. Для этого можно использовать библиотеку <code class="language-plaintext highlighter-rouge">librosa</code>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">glob</span>
<span class="kn">import</span> <span class="nn">librosa</span>
<span class="kn">import</span> <span class="nn">soundfile</span> <span class="k">as</span> <span class="n">sf</span>
<span class="n">target_sr</span> <span class="o">=</span> <span class="mi">8000</span>
<span class="k">for</span> <span class="n">fn</span> <span class="ow">in</span> <span class="n">glob</span><span class="p">.</span><span class="n">glob</span><span class="p">(</span><span class="s">"./audio/*.opus"</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Processing </span><span class="si">{</span><span class="n">fn</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">au</span><span class="p">,</span><span class="n">sr</span> <span class="o">=</span> <span class="n">librosa</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span><span class="n">sr</span><span class="o">=</span><span class="n">target_sr</span><span class="p">)</span>
<span class="n">sf</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">fn</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'.opus'</span><span class="p">,</span><span class="s">'.ogg'</span><span class="p">),</span><span class="n">au</span><span class="p">,</span>
<span class="n">target_sr</span><span class="p">,</span><span class="nb">format</span><span class="o">=</span><span class="s">'ogg'</span><span class="p">,</span><span class="n">subtype</span><span class="o">=</span><span class="s">'opus'</span><span class="p">)</span>
</code></pre></div></div>
<p>В результате получаем набор файлов с расширением <code class="language-plaintext highlighter-rouge">ogg</code>, которые можно подавать на вход Yandex Speechkit. Поскольку речь идёт о распознавании большого объема текста, будем использовать <a href="https://cloud.yandex.ru/docs/speechkit/stt/transcribation">асинхронное распознавание</a> (<em>транскрибацию</em>): для этого необходимо положить все файлы в хранилище S3, запустить процесс распознавания, и затем периодически проверять результаты.</p>
<p>При использовании DataSphere, скопировать файлы в S3 проще всего, подключив некоторое хранилище к DataSphere через S3-коннектор. Предположим, мы смонтировали бакет <code class="language-plaintext highlighter-rouge">mclass</code> в директорию <code class="language-plaintext highlighter-rouge">mclass</code>, в этом случае файлы можно переместить простым копированием:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">!</span><span class="n">mkdir</span> <span class="o">-</span><span class="n">p</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">jupyter</span><span class="o">/</span><span class="n">mnt</span><span class="o">/</span><span class="n">s3</span><span class="o">/</span><span class="n">mclass</span><span class="o">/</span><span class="n">audio</span>
<span class="err">!</span><span class="n">cp</span> <span class="p">.</span><span class="o">/</span><span class="n">audio</span><span class="o">/*</span><span class="p">.</span><span class="n">ogg</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">jupyter</span><span class="o">/</span><span class="n">mnt</span><span class="o">/</span><span class="n">s3</span><span class="o">/</span><span class="n">mclass</span><span class="o">/</span><span class="n">audio</span>
</code></pre></div></div>
<p>Для запуска распознавания опишем функцию <code class="language-plaintext highlighter-rouge">submit_for_sr</code>, которая будет формировать запрос в соответствии <a href="https://cloud.yandex.ru/docs/speechkit/stt/api/transcribation-api">с этим API</a>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">submit_for_sr</span><span class="p">(</span><span class="n">audio_file</span><span class="p">):</span>
<span class="n">j</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"config"</span><span class="p">:</span> <span class="p">{</span>
<span class="s">"specification"</span><span class="p">:</span> <span class="p">{</span> <span class="s">"languageCode"</span><span class="p">:</span> <span class="s">"ru-RU"</span> <span class="p">}},</span>
<span class="s">"audio"</span><span class="p">:</span> <span class="p">{</span> <span class="s">"uri"</span><span class="p">:</span> <span class="n">audio_file</span> <span class="p">}}</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span>
<span class="s">"https://transcribe.api.cloud.yandex.net/speech/stt/v2/longRunningRecognize"</span><span class="p">,</span>
<span class="n">json</span> <span class="o">=</span> <span class="n">j</span><span class="p">,</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"Authorization"</span> <span class="p">:</span> <span class="sa">f</span><span class="s">"Api-Key </span><span class="si">{</span><span class="n">api_key</span><span class="si">}</span><span class="s">"</span> <span class="p">})</span>
<span class="k">return</span> <span class="n">res</span><span class="p">.</span><span class="n">json</span><span class="p">()[</span><span class="s">'id'</span><span class="p">]</span>
</code></pre></div></div>
<p>При этом для работы этой функции нам надо будет создать в нашем облаке <a href="https://cloud.yandex.ru/docs/iam/concepts/users/service-accounts">сервисный аккаунт</a>, имеющий доступ к функции распознавания речи, чтения хранилища и работы с языковыми моделями, и создать <a href="https://cloud.yandex.ru/docs/iam/operations/api-key/create">ключ API</a> для этого аккаунта. Предполагается, что переменная <code class="language-plaintext highlighter-rouge">api_key</code> содержит этот ключ.</p>
<p>Для посылки всех файлов на распознавание, используем обычный цикл:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">d</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">fn</span> <span class="ow">in</span> <span class="n">glob</span><span class="p">.</span><span class="n">glob</span><span class="p">(</span><span class="s">'/home/jupyter/mnt/s3/mclass/audio/*.ogg'</span><span class="p">):</span>
<span class="n">ext_name</span> <span class="o">=</span> <span class="n">fn</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'/home/jupyter/mnt/s3/'</span><span class="p">,</span>
<span class="s">'https://storage.yandexcloud.net/'</span><span class="p">)</span>
<span class="nb">id</span> <span class="o">=</span> <span class="n">submit_for_sr</span><span class="p">(</span><span class="n">ext_name</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Submitted </span><span class="si">{</span><span class="n">fn</span><span class="si">}</span><span class="s"> -> </span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">d</span><span class="p">[</span><span class="nb">id</span><span class="p">]</span> <span class="o">=</span> <span class="n">fn</span>
</code></pre></div></div>
<p>В этом коде с помощью <code class="language-plaintext highlighter-rouge">replace</code> мы заменяем локальный путь к файлу на URL-ссылку на файл в хранилище S3. Хранилище при этом не должно быть открыто для чтения - нужный доступ будет автоматически предоставлен соответствующему сервисному аккаунту.</p>
<p>В результате, в словаре <code class="language-plaintext highlighter-rouge">d</code> окажутся списки идентификаторов процессов распознавания, и соответствующие пути файлов. Чтобы проверить готовность распознавания, определим такую функцию:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">check_ready</span><span class="p">(</span><span class="nb">id</span><span class="p">):</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"https://operation.api.cloud.yandex.net/operations/</span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"Authorization"</span> <span class="p">:</span> <span class="sa">f</span><span class="s">"Api-Key </span><span class="si">{</span><span class="n">api_key</span><span class="si">}</span><span class="s">"</span> <span class="p">})</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">json</span><span class="p">()</span>
<span class="k">if</span> <span class="n">res</span><span class="p">[</span><span class="s">'done'</span><span class="p">]:</span>
<span class="k">return</span> <span class="n">res</span><span class="p">[</span><span class="s">'response'</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">None</span>
</code></pre></div></div>
<p>Пока результат не готов, эта функция будет возвращать <code class="language-plaintext highlighter-rouge">None</code>, а после готовности вернёт JSON-файл с распознанными фрагментами.</p>
<p>Теперь реализуем код, который проверяет все распознавания на готовность, а в случае готовности помещает результат в словарь <code class="language-plaintext highlighter-rouge">txt</code>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">txt</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">for</span> <span class="n">k</span><span class="p">,</span><span class="n">v</span> <span class="ow">in</span> <span class="n">d</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">txt</span><span class="p">.</span><span class="n">keys</span><span class="p">():</span>
<span class="k">continue</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">check_ready</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
<span class="k">if</span> <span class="n">res</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s"> -> waiting"</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s"> -> ready"</span><span class="p">)</span>
<span class="n">txt</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="o">=</span> <span class="s">' '</span><span class="p">.</span><span class="n">join</span><span class="p">([</span><span class="n">x</span><span class="p">[</span><span class="s">'alternatives'</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s">'text'</span><span class="p">]</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">res</span><span class="p">[</span><span class="s">'chunks'</span><span class="p">]])</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">txt</span><span class="p">.</span><span class="n">keys</span><span class="p">())</span><span class="o">==</span><span class="nb">len</span><span class="p">(</span><span class="n">d</span><span class="p">.</span><span class="n">keys</span><span class="p">()):</span>
<span class="k">break</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
</code></pre></div></div>
<p>Когда все результаты получены, нам осталось лишь сохранить текстовые файлы:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">k</span><span class="p">,</span><span class="n">v</span> <span class="ow">in</span> <span class="n">txt</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">k</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'.ogg'</span><span class="p">,</span><span class="s">'.txt'</span><span class="p">)</span>
<span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'/audio/'</span><span class="p">,</span><span class="s">'/text/'</span><span class="p">),</span>
<span class="s">'w'</span><span class="p">,</span><span class="n">encoding</span><span class="o">=</span><span class="s">'utf-8'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="разбиваем-текст-на-фрагменты">Разбиваем текст на фрагменты</h2>
<p>На предыдущем этапе, мы получили набор текстовых файлов, по одному на видео. Однако, они скорее всего слишком велики, чтобы быть использованными для запросов. Дело в том, что у нас есть два ограничения:</p>
<ul>
<li><strong>Размер контекста эмбеддинга</strong> показывает, сколько токенов мы можем использовать для вычисления смыслового вектора. Обычно, размер контекста эмбеддинга не слишком велик - от 512 токенов, до 2048.</li>
</ul>
<blockquote>
<p>Токен - это единица входного текста, подающаяся на вход нейросетевой модели. Обычно, токеном является слово, или чаще часть слова. Например, в модели Yandex GPT длина одного токена обычно составляет около 3 символов.</p>
</blockquote>
<ul>
<li><strong>Размер контекста языковой модели</strong>, т.е. насколько длинным может быть запрос (или запрос+ответ). Для Yandex GPT длина контекста запроса+ответа составляет чуть более 7000 токенов, и в эти токены должны входить 3-5 лучших найденных фрагментов текста, сам запрос с инструкциями, и выдаваемый пользователю ответ.</li>
</ul>
<p>Исходя из этих соображений, длина фрагмента текста обычно выбирается 512-2048 токенов. Иногда проще задавать эту длину в символах, поскольку не всегда заранее очевидно, как будет токенизирован текст.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">langchain</span>
<span class="kn">import</span> <span class="nn">langchain.document_loaders</span>
<span class="n">source_dir</span> <span class="o">=</span> <span class="s">"/home/jupyter/mnt/s3/mclass/text"</span>
<span class="n">loader</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">document_loaders</span><span class="p">.</span><span class="n">DirectoryLoader</span><span class="p">(</span>
<span class="n">source_dir</span><span class="p">,</span><span class="n">glob</span><span class="o">=</span><span class="s">"*.txt"</span><span class="p">,</span>
<span class="n">show_progress</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span><span class="n">recursive</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">splitter</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">text_splitter</span><span class="p">.</span><span class="n">RecursiveCharacterTextSplitter</span><span class="p">(</span>
<span class="n">chunk_size</span><span class="o">=</span><span class="mi">1024</span><span class="p">,</span><span class="n">chunk_overlap</span><span class="o">=</span><span class="mi">128</span><span class="p">)</span>
<span class="n">fragments</span> <span class="o">=</span> <span class="n">splitter</span><span class="p">.</span><span class="n">create_documents</span><span class="p">(</span>
<span class="p">[</span> <span class="n">x</span><span class="p">.</span><span class="n">page_content</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">loader</span><span class="p">.</span><span class="n">load</span><span class="p">()</span> <span class="p">])</span>
</code></pre></div></div>
<p>В данном случае мы используем удобный класс <code class="language-plaintext highlighter-rouge">RecursiveCharacterTextSplitter</code>, который сначала пытается разбить текст по большим разделителям (абзацам), потом - по разделителям между предложениями, и в худшем случае использует разделители между словами. Это позволяет получить наиболее осмысленные фрагменты текста.</p>
<p>В результате у нас получится переменная <code class="language-plaintext highlighter-rouge">fragments</code>, содержащая фрагменты текста.</p>
<blockquote>
<p>В реальных проектах, при индексировании большого объема документов, не стоит рассчитывать на то, что они все поместятся в память. В этом случае разбиение на фрагменты нужно совмещать с помещением фрагментов в векторную базу данных, которая хранится на диске.</p>
</blockquote>
<h2 id="вычисляем-эмбеддинги">Вычисляем эмбеддинги</h2>
<p>Вычисление эмбеддингов - это достаточно важная задачи, и подобрать оптимальный вариант для русского языка непросто. LangChain содержит много готовых классов, которые позволяют вычислять эмбеддинги как локально с помощью предобученных (или даже обученных вами) моделей, так и с помощью онлайн-сервисов, таких, как OpenAI.</p>
<p>Я остановлюсь на двух вариантах вычисления эмбеддингов:</p>
<ul>
<li>Использовать какую-нибудь модель с HuggingFace с поддержкой русского языка. LangChain позволяет вычислять эмбеддинги с помощью HuggingFace-моделей в пару строк кода. Размер контекста такой модели как правило не очень велик, поэтому нужно будет соответствующим образом подобрать <code class="language-plaintext highlighter-rouge">chunk_size</code> в коде выше при разбиении текста</li>
</ul>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">embeddings</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">embeddings</span><span class="p">.</span><span class="n">HuggingFaceEmbedding</span><span class="p">(</span>
<span class="n">model_name</span><span class="o">=</span><span class="s">"distiluse-base-multilingual-cased-v1"</span><span class="p">)</span>
<span class="n">sample_vec</span> <span class="o">=</span> <span class="n">embeddings</span><span class="p">.</span><span class="n">embed_query</span><span class="p">(</span><span class="s">"Hello, world!"</span><span class="p">)</span>
</code></pre></div></div>
<ul>
<li>Использовать <a href="https://cloud.yandex.ru/docs/yandexgpt/api-ref/Embeddings/">сервис для вычисления эмбеддингов от Yandex GPT</a>. В этом случае нам нужно будет самим реализовать адаптер для вычисления эмбеддингов в LangChain, унаследовав его от <code class="language-plaintext highlighter-rouge">langchain.embeddings.base.Embeddings</code>:</li>
</ul>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">langchain.embeddings.base</span> <span class="kn">import</span> <span class="n">Embeddings</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">class</span> <span class="nc">YaGPTEmbeddings</span><span class="p">(</span><span class="n">Embeddings</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span><span class="n">folder_id</span><span class="p">,</span><span class="n">api_key</span><span class="p">,</span><span class="n">sleep_interval</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
<span class="bp">self</span><span class="p">.</span><span class="n">folder_id</span> <span class="o">=</span> <span class="n">folder_id</span>
<span class="bp">self</span><span class="p">.</span><span class="n">api_key</span> <span class="o">=</span> <span class="n">api_key</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sleep_interval</span> <span class="o">=</span> <span class="n">sleep_interval</span>
<span class="bp">self</span><span class="p">.</span><span class="n">headers</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"Authorization"</span> <span class="p">:</span> <span class="sa">f</span><span class="s">"Api-key </span><span class="si">{</span><span class="n">api_key</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
<span class="s">"x-folder-id"</span> <span class="p">:</span> <span class="n">folder_id</span> <span class="p">}</span>
<span class="k">def</span> <span class="nf">embed_document</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">text</span><span class="p">):</span>
<span class="n">j</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"model"</span> <span class="p">:</span> <span class="s">"general:embedding"</span><span class="p">,</span>
<span class="s">"embedding_type"</span> <span class="p">:</span> <span class="s">"EMBEDDING_TYPE_DOCUMENT"</span><span class="p">,</span>
<span class="s">"text"</span><span class="p">:</span> <span class="n">text</span>
<span class="p">}</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span>
<span class="s">"https://llm.api.cloud.yandex.net/llm/v1alpha/embedding"</span><span class="p">,</span>
<span class="n">json</span><span class="o">=</span><span class="n">j</span><span class="p">,</span><span class="n">headers</span><span class="o">=</span><span class="bp">self</span><span class="p">.</span><span class="n">headers</span><span class="p">)</span>
<span class="n">vec</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">json</span><span class="p">()[</span><span class="s">'embedding'</span><span class="p">]</span>
<span class="k">return</span> <span class="n">vec</span>
<span class="k">def</span> <span class="nf">embed_documents</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">texts</span><span class="p">,</span> <span class="n">chunk_size</span> <span class="o">=</span> <span class="mi">0</span><span class="p">):</span>
<span class="n">res</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">texts</span><span class="p">:</span>
<span class="n">res</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">embed_document</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">sleep_interval</span><span class="p">)</span>
<span class="k">return</span> <span class="n">res</span>
<span class="k">def</span> <span class="nf">embed_query</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">text</span><span class="p">):</span>
<span class="n">j</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"model"</span> <span class="p">:</span> <span class="s">"general:embedding"</span><span class="p">,</span>
<span class="s">"embedding_type"</span> <span class="p">:</span> <span class="s">"EMBEDDING_TYPE_QUERY"</span><span class="p">,</span>
<span class="s">"text"</span><span class="p">:</span> <span class="n">text</span>
<span class="p">}</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span>
<span class="s">"https://llm.api.cloud.yandex.net/llm/v1alpha/embedding"</span><span class="p">,</span>
<span class="n">json</span><span class="o">=</span><span class="n">j</span><span class="p">,</span><span class="n">headers</span><span class="o">=</span><span class="bp">self</span><span class="p">.</span><span class="n">headers</span><span class="p">)</span>
<span class="n">vec</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">json</span><span class="p">()[</span><span class="s">'embedding'</span><span class="p">]</span>
<span class="k">return</span> <span class="n">vec</span>
<span class="n">embeddings</span> <span class="o">=</span> <span class="n">YaGPTEmbeddings</span><span class="p">(</span><span class="n">folder_id</span><span class="p">,</span><span class="n">api_key</span><span class="p">)</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">embeddings</span><span class="p">.</span><span class="n">embed_documents</span><span class="p">([</span><span class="s">'Hello'</span><span class="p">,</span><span class="s">'there'</span><span class="p">])</span>
</code></pre></div></div>
<blockquote>
<p><strong>Обновление от октября 2023</strong>: Вместо того, чтобы самим реализовывать класс для вычисления эмбеддингов, можно воспользоваться библиотекой <a href="https://github.com/yandex-datasphere/yandex-chain">yandex-chain</a>, которая содержит более надёжную реализацию, чем приведённая выше.</p>
</blockquote>
<p>Этот класс содержит два основных метода, каждый из которых вызывает соответствующее API Yandex Cloud:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">embed_query</code> используется для вычисления эмбеддинга запроса</li>
<li><code class="language-plaintext highlighter-rouge">embed_documents</code> используется для вычисления эмбеддинга семейства документов. Поскольку Yandex API поддерживает вычисление эмбеддинга только для одного документа за вызов, то этот метод реализован как цикл, вызывающий метод <code class="language-plaintext highlighter-rouge">embed_document</code> для каждого документа в коллекции.</li>
</ul>
<blockquote>
<p>Поскольку в настоящее время доступ к сервису лимитирован 1 запросом в секунду, между вызовами добавлена задержка.</p>
</blockquote>
<p>В следующем разделе нам понадобится вычислять эмбеддинги. Для этого используем переменную <code class="language-plaintext highlighter-rouge">embeddings</code> - вы можете использовать один из двух предложенных выше вариантов на выбор.</p>
<h2 id="сохраняем-документы-в-векторную-бд">Сохраняем документы в векторную БД</h2>
<p>LangChain поддерживает множество векторных БД, от очень простой и легковесной <a href="https://github.com/chroma-core/chroma">ChromaDB</a>, до большого решения на кластере <a href="https://opensearch.org/">OpenSearch</a>. Выбирать решение можно исходя из сложности задачи и объема данных.</p>
<blockquote>
<p>Если Вы строите решение в облаке Yandex Cloud, то можно использовать <a href="https://cloud.yandex.ru/services/managed-opensearch">управляемый OpenSearch в Yandex Cloud</a> - это упростит управление, и позволит отдать масштабирование на откуп облачным сервисам.</p>
</blockquote>
<p>В нашем примере мы используем <a href="https://lancedb.com/">LanceDB</a>, поскольку она позволяет сохранять базу данных в обычной директории. Для начала создадим таблицу:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">langchain.vectorstores</span> <span class="kn">import</span> <span class="n">LanceDB</span>
<span class="kn">import</span> <span class="nn">lancedb</span>
<span class="n">db_dir</span> <span class="o">=</span> <span class="s">"../store"</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">lancedb</span><span class="p">.</span><span class="n">connect</span><span class="p">(</span><span class="n">db_dir</span><span class="p">)</span>
<span class="n">table</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">create_table</span><span class="p">(</span>
<span class="s">"vector_index"</span><span class="p">,</span>
<span class="n">data</span><span class="o">=</span><span class="p">[{</span>
<span class="s">"vector"</span><span class="p">:</span> <span class="n">embeddings</span><span class="p">.</span><span class="n">embed_query</span><span class="p">(</span><span class="s">"Hello World"</span><span class="p">),</span>
<span class="s">"text"</span><span class="p">:</span> <span class="s">"Hello World"</span><span class="p">,</span>
<span class="s">"id"</span><span class="p">:</span> <span class="s">"1"</span><span class="p">,</span>
<span class="p">}],</span>
<span class="n">mode</span><span class="o">=</span><span class="s">"overwrite"</span><span class="p">)</span>
</code></pre></div></div>
<p>Чтобы проиндексировать все документы, используем возможности LangChain:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">db</span> <span class="o">=</span> <span class="n">LanceDB</span><span class="p">.</span><span class="n">from_documents</span><span class="p">(</span><span class="n">fragments</span><span class="p">,</span> <span class="n">embeddings</span><span class="p">,</span> <span class="n">connection</span><span class="o">=</span><span class="n">table</span><span class="p">)</span>
</code></pre></div></div>
<p>Теперь, чтобы найти ближайшие по расстоянию документы, можно использовать метод <code class="language-plaintext highlighter-rouge">similarity_search</code>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">q</span><span class="o">=</span><span class="s">"Чем iPhone лучше Samsung?"</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">similarity_search</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">res</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'-'</span><span class="o">*</span><span class="mi">40</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">page_content</span><span class="p">)</span>
</code></pre></div></div>
<p>Можно также использовать интерфейс <code class="language-plaintext highlighter-rouge">retriever</code>, позволяющий задать различные стратегии и параметры поиска, например:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">retriever</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">as_retriever</span><span class="p">(</span>
<span class="n">search_kwargs</span><span class="o">=</span><span class="p">{</span><span class="s">"k"</span><span class="p">:</span> <span class="mi">5</span><span class="p">})</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">retriever</span><span class="p">.</span><span class="n">get_relevant_documents</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="учимся-работать-с-yandex-gpt">Учимся работать с Yandex GPT</h2>
<p>Также как и в случае с эмбеддингами, LangChain не содержит встроенных инструментов работы с генеративной языковой моделью Yandex GPT. Поэтому нам нужно будет реализовать адаптер самостоятельно, в соответствии <a href="https://python.langchain.com/docs/modules/model_io/models/llms/custom_llm">с документацией</a>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">YandexLLM</span><span class="p">(</span><span class="n">langchain</span><span class="p">.</span><span class="n">llms</span><span class="p">.</span><span class="n">base</span><span class="p">.</span><span class="n">LLM</span><span class="p">):</span>
<span class="n">api_key</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="bp">None</span>
<span class="n">folder_id</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">max_tokens</span> <span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1500</span>
<span class="n">temperature</span> <span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">instruction_text</span> <span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">def</span> <span class="nf">_call</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">)</span> <span class="p">:</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"x-folder-id"</span> <span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">folder_id</span> <span class="p">,</span>
<span class="s">"Authorization"</span> <span class="p">:</span> <span class="sa">f</span><span class="s">"Api-key </span><span class="si">{</span><span class="bp">self</span><span class="p">.</span><span class="n">api_key</span><span class="si">}</span><span class="s">"</span><span class="p">}</span>
<span class="n">req</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"model"</span><span class="p">:</span> <span class="s">"general"</span><span class="p">,</span>
<span class="s">"instruction_text"</span><span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">instruction_text</span><span class="p">,</span>
<span class="s">"request_text"</span><span class="p">:</span> <span class="n">prompt</span><span class="p">,</span>
<span class="s">"generation_options"</span><span class="p">:</span> <span class="p">{</span>
<span class="s">"max_tokens"</span><span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">max_tokens</span><span class="p">,</span>
<span class="s">"temperature"</span><span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">temperature</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span>
<span class="s">"https://llm.api.cloud.yandex.net/llm/v1alpha/instruct"</span><span class="p">,</span>
<span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">req</span><span class="p">).</span><span class="n">json</span><span class="p">()</span>
<span class="k">return</span> <span class="n">res</span><span class="p">[</span><span class="s">'result'</span><span class="p">][</span><span class="s">'alternatives'</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s">'text'</span><span class="p">]</span>
</code></pre></div></div>
<p>Этот код для ясности немного упрощен, более полная версия содержится <a href="https://github.com/yandex-datasphere/VideoQABot/blob/main/YaGPT.py">в репозитории GitHub</a></p>
<blockquote>
<p><strong>Обновление от октября 2023</strong>: Вместо того, чтобы самим реализовывать класс <code class="language-plaintext highlighter-rouge">YandexLLM</code>, можно воспользоваться библиотекой <a href="https://github.com/yandex-datasphere/yandex-chain">yandex-chain</a>, которая содержит более надёжную реализацию, чем приведённая выше. Кроме того, поддержка Yandex GPT есть в библиотеке <a href="https://github.com/ai-forever/gigachain">GigaChain</a>, реализации LangChain от Сбер.</p>
</blockquote>
<p>Теперь мы можем работать с моделью следующим образом:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">instructions</span> <span class="o">=</span> <span class="s">"""
Представь себе, что ты технический блоггер, который делает обзоры
современной электроники. Тебя спрашивает знакомый. Постарайся
ответить на его вопрос подробно и доступно."""</span>
<span class="n">llm</span> <span class="o">=</span> <span class="n">YandexLLM</span><span class="p">(</span><span class="n">api_key</span><span class="o">=</span><span class="n">api_key</span><span class="p">,</span> <span class="n">folder_id</span><span class="o">=</span><span class="n">folder_id</span><span class="p">,</span>
<span class="n">instruction_text</span> <span class="o">=</span> <span class="n">instructions</span><span class="p">)</span>
<span class="n">llm</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
</code></pre></div></div>
<p>Если мы зададим вопрос про то, чем iPhone лучше Samsung, то можем получить примерно такой ответ:</p>
<p><em>
Приветствую! Сегодня я хочу сравнить два популярных смартфона - iPhone и Samsung. Оба варианта имеют свои преимущества и недостатки.<br />
Начнем с дизайна. В отличие от многих Samsung, называемых “кирпичами”, комбинация материалов iPhone создает ощущение минималистичности и элегантности. Кроме того, iPhone преданы монобровь, функция Raise to wake, чтобы вы могли насладиться полноэкранным просмотром видео или изображений без черных платаповков во время эксплуатации, удобен в использовании, является универсальным (работает все известные Приложения)<br />
Дизайн Samsung Galaxy имеет высоко-концентрированный документ Center Wheel и Trade приложение для переднего управления, которое удобно используется для определения яркости, открытия приложения многозадачности. Он также имеет встроенную камеру со множеством эффектов снимков, которые забавляют для вас. К этим изменениям можно добавить датчик шагового.
</em></p>
<h2 id="реализуем-retrieval-augmented-generation">Реализуем Retrieval-Augmented Generation</h2>
<p>В коде выше мы уже нашли 5 наиболее подходящих фрагментов текста для нашего запроса <code class="language-plaintext highlighter-rouge">q</code>, и они находятся в переменной <code class="language-plaintext highlighter-rouge">res</code>. Каждый документ имеет поле <code class="language-plaintext highlighter-rouge">page_content</code> с текстом страницы.</p>
<p>Retrieval-Augmented Generation можно реализовать с помощью механизма цепочек (<em>chain</em>) и метода <code class="language-plaintext highlighter-rouge">StuffDocumentsChain</code>, который делает следующее:</p>
<ol>
<li>Берёт коллекцию документов input_documents</li>
<li>Каждый из них пропускает через некоторый шаблон <code class="language-plaintext highlighter-rouge">document_prompt</code>, и затем объединяет вместе.</li>
<li>Данный текст помещается в переменную <code class="language-plaintext highlighter-rouge">document_variable_name</code> и передаётся большой языковой модели <code class="language-plaintext highlighter-rouge">llm</code></li>
</ol>
<p>В нашем случае <code class="language-plaintext highlighter-rouge">document_prompt</code> не будет модифицировать документ, а будет просто возвращать его без изменений:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Промпт для обработки документов
</span><span class="n">document_prompt</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">prompts</span><span class="p">.</span><span class="n">PromptTemplate</span><span class="p">(</span>
<span class="n">input_variables</span><span class="o">=</span><span class="p">[</span><span class="s">"page_content"</span><span class="p">],</span> <span class="n">template</span><span class="o">=</span><span class="s">"{page_content}"</span><span class="p">)</span>
</code></pre></div></div>
<p>Для формирования окончательного ответа мы используем более сложный шаблон, который принимает на вход пользовательский запрос <code class="language-plaintext highlighter-rouge">query</code> и контекст <code class="language-plaintext highlighter-rouge">context</code> (это как раз найденные наиболее релевантные фрагменты текста):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Промпт для языковой модели
</span><span class="n">document_variable_name</span> <span class="o">=</span> <span class="s">"context"</span>
<span class="n">template</span> <span class="o">=</span> <span class="s">"""
Пожалуйста, посмотри на текст ниже и ответь на вопрос, используя
информацию из этого текста.
Текст:
-----
{context}
-----
Вопрос:
{query}"""</span>
<span class="n">prompt</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">prompts</span><span class="p">.</span><span class="n">PromptTemplate</span><span class="p">(</span>
<span class="n">template</span><span class="o">=</span><span class="n">template</span><span class="p">,</span> <span class="n">input_variables</span><span class="o">=</span><span class="p">[</span><span class="s">"context"</span><span class="p">,</span> <span class="s">"query"</span><span class="p">])</span>
</code></pre></div></div>
<p>Далее мы создаём цепочку <code class="language-plaintext highlighter-rouge">llm_chain</code>, которая вызывает описанную нами ранее языковую модель Yandex GPT <code class="language-plaintext highlighter-rouge">llm</code> с шаблоном <code class="language-plaintext highlighter-rouge">prompt</code>, после чего инициализируем основную цепочку <code class="language-plaintext highlighter-rouge">chain</code></p>
<pre><code class="language-pyhon"># Создаём цепочку
llm_chain = langchain.chains.LLMChain(llm=llm, prompt=prompt)
chain = langchain.chains.StuffDocumentsChain(
llm_chain=llm_chain,
document_prompt=document_prompt,
document_variable_name=document_variable_name)
</code></pre>
<p>Теперь, чтобы получить ответ на запрос <code class="language-plaintext highlighter-rouge">q</code> с коллекцией документов <code class="language-plaintext highlighter-rouge">res</code>, мы просто запускаем цепочку:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">chain</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">input_documents</span><span class="o">=</span><span class="n">res</span><span class="p">,</span> <span class="n">query</span><span class="o">=</span><span class="n">q</span><span class="p">)</span>
</code></pre></div></div>
<p>Вот полученный в моём случае ответ:</p>
<p><em>
Если вы хотите продать свой смартфон через продолжительное время в будущем, я бы рекомендовал инвестировать в Apple, потому что после выпуска недавно нового iphone SE - они все еще способны обеспечивать достойную стоимость устройств по прошествии времени. Кроме того, смартфоны apple показывают большую стабильность в основных системах и приложениях. Несмотря на эти ограничения, возможность обновления devices со временем может быть стимулом для тех, кто ищет долгосрочную инвестиционную цель для своих устройств.<br />
Если ценность телефона является ключевым фактором, я бы порекомендовал рассматривать модели с лучшими ЖК-дисплеями в вашем бюджете, чтобы рассмотреть долгосрочный обмен. Samsung все еще остается лидером в производстве дисплеев, особенно в их линейке galaxy S.<br />
Также я бы обращал внимание на программное обеспечение iOS, которое предоставляет множество дополнительных возможностей, хотя отсутствие обновления могла бы смутить некоторых пользователей. </em></p>
<p>Субъективно кажется, что этот ответ существенно более глубокий, вероятно из-за того, что он учитывает высказанные Wylsacom экспертные замечания.</p>
<h2 id="преобразование-контекста">Преобразование контекста</h2>
<p>Теперь нам осталось собрать всё вместе и реализовать одну функцию <code class="language-plaintext highlighter-rouge">answer</code>, которая будет отвечать на вопрос пользователя.</p>
<p>Однако, добавим ещё один небольшой финальный штрих - это изменение порядка фрагментов в контексте. Дело в том, что часто языковые модели устроены таким образом, что максимально значимыми фрагментами запроса является его начало и конец, в связи с чем логичнее располагать более релевантные фрагменты текста ближе к началу и концу запроса. Класс <code class="language-plaintext highlighter-rouge">LongContextReorder</code> позволяет нам легко реализовать такое перемешивание найденных фрагментов текста.</p>
<p>С учётом сказанного, функция <code class="language-plaintext highlighter-rouge">answer</code> будет выглядеть так:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">langchain.document_transformers</span> <span class="kn">import</span> <span class="n">LongContextReorder</span>
<span class="n">reorderer</span> <span class="o">=</span> <span class="n">LongContextReorder</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">answer</span><span class="p">(</span><span class="n">query</span><span class="p">,</span><span class="n">reorder</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">retriever</span><span class="p">.</span><span class="n">get_relevant_documents</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
<span class="k">if</span> <span class="n">reorder</span><span class="p">:</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">reorderer</span><span class="p">.</span><span class="n">transform_documents</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
<span class="k">return</span> <span class="n">chain</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">input_documents</span><span class="o">=</span><span class="n">results</span><span class="p">,</span> <span class="n">query</span><span class="o">=</span><span class="n">query</span><span class="p">)</span>
</code></pre></div></div>
<p>Также мы можем реализовать функцию для сравнения ответов “голой” языковой модели, и нашего бота с Retrieval-Augmented Generation:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">compare</span><span class="p">(</span><span class="n">q</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Ответ YaGPT: </span><span class="si">{</span><span class="n">llm</span><span class="p">(</span><span class="n">q</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Ответ бота: </span><span class="si">{</span><span class="n">answer</span><span class="p">(</span><span class="n">q</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">compare</span><span class="p">(</span><span class="s">"Какой макбук лучше выбрать студенту?"</span><span class="p">)</span>
</code></pre></div></div>
<p>Смотрите на результаты:</p>
<table>
<tbody>
<tr>
<td>Yandex GPT</td>
<td>Наш бот</td>
</tr>
<tr>
<td>Выбор Макбука для студента зависит от нескольких факторов, таких как бюджет, требования к производительности и задачам, которые предстоит решать в университете. Важно учитывать технические характеристики, такие как процессор, оперативная память, жесткий диск, а также другие особенности, такие как размер экрана, графика и другие. Если студенты ищут недорогое устройство, то Apple Macbook Air может быть хорошим выбором. Он имеет легкий корпус, маленький экран и низкое электропотребление, что позволяет использовать его в дороге с легкостью. Однако, если нужен более мощный ноутбук для рабочих задач, тогда стоит рассмотреть Macbook Pro. У него более производительный процессор и большой объем оперативной памяти, что позволит выполнять сложные задачи без задержек. Также стоит учитывать, что экран у Macbook Pro больше и разрешение выше, что делает работу более комфортной. Наконец, если необходимо иметь ноутбук с возможностью расширения оперативной памяти и хранения данных, следует обратить внимание на Macbook Pro с дополнительным слотом для расширения. Это обеспечивает дополнительные возможности для производительности при работе с требовательными приложениями.</td>
<td>Как технический блогер, специализирующийся на обзорах современной электроники, скажу, что для студента лучше выбирать между MacBook Air 13” M1 и MacBook Pro 13” M2. Если рассматривать первый вариант, то он будет отличным выбором для студентов, так как он имеет стильный дизайн, хорошее качество сборки, быстрый процессор M1, достаточный объем встроенной памяти и быстрый SSD-диск. Так же в сравнение с другими ноутбуками этого же класса он выигрывает по результатам бенчмарков и тестов на скорость работы. Из минусов — несколько устаревший дизайн ноутбука в сравнении с более новыми моделями и отсутствие встроенного WIFI 6. Кроме того, немного смущает цена этого ноутбука. Однако он является идеальным ноутбуком для учебы и работы. К тому же, этот ноутбук легче своего предыдущего варианта — MacBook Air на процессоре Intel, что делает его хорошим выбором для мобильного использования. Один из главных вариантов такой организации учебного процесса в вузах – это дистанционное образование. Хотя карантинная ситуация всё еще остается в центре внимания многих людей, а школы вынужденно переходят на дистанционное обучение, университеты тоже старались максимально ускорить переход своих студентов на удаленную учебную систему. Средний студент легко приобретает специальный деcктоп или ноутбук, на котором можно было бы получать электронную почту. Те, кто ранее приобретали мышкой и клавиатурой ноутбуки, в том числе студенты младших курсов лишились ноутбука, чтобы довольствоваться обыкновенным компьютером. Также это вариант учебного плана, где лекции проходят онлайн-трансляция учителей. Практические занятия проходят с использованием планшетов и ноутбуков со своим ПО.</td>
</tr>
</tbody>
</table>
<h2 id="выводы">Выводы</h2>
<p>В данной статье мы попытались решить весьма сложную задачу - создание вопрос-ответного бота на основе видео. Одна из проблем здесь кроется в том, что результаты распознавания не всегда идеальны, особенно по части пунктуации и форматирования (абзацы, заголовки и др.). Кроме того, видео содержат некоторое количество слов-паразитов, неверно построенных грамматических фрагментов и т.д.</p>
<p>С учетом этого особенно приятно, что результат получился неплохой. В последнем примере мы видим, что при выборе ноутбука начинают учитываться специфические факторы, такие, как онлайн-занятия в пандемию. Очевидно, что именно такие тонкости отличают взгляд настоящего эксперта от “рекламного текста”, на который в большей степени похож ответ Yandex GPT.</p>
<p>Ещё раз напомню читателям, что весь код из статьи можно найти <a href="http://github.com/yandex-datasphere/VideoQABot">в репозитории</a>.</p>
2023-09-06T00:00:00+00:00https://soshnikov.com/ai/creating-problem-domain-specific-chat-assistant-with-yandex-gpt-and-langchain-en/Creating Domain-Oriented Chatbots using LangChain and Yandex GPT2023-09-06T00:00:00+00:00Dmitri Soshnikovhttp://soshnikov.com/Large language models handle conversations on general topics perfectly, but often it is necessary to add some domain knowledge to a chatbot. Let's see how to use the Retrieval-Augmented Generation approach to create such a chatbot based on the LangChain framework and the Yandex GPT generative model<h2 id="how-to-create-domain-specific-question-answer-model">How to Create Domain Specific Question-Answer Model</h2>
<p>To date, advanced conversational models use large language models (LLM), such as <a href="/ai/how-to-use-chatgpt-ru/">ChatGPT</a>, <a href="https://cloud.yandex.ru/services/yandexgpt">Yandex GPT</a>, <a href="https://developers.sber.ru/portal/products/gigachat">GigaChat</a>, etc. Such models are trained on huge amounts of data, they are able to carry out a dialogue on general topics perfectly. However, in practice, there are often tasks when we want to create a dialog model that can talk about some specific topics - for example, answer questions about the company’s products, or recommend where to buy medicine in accordance with current availability data from the database.</p>
<p>Such chatbots can be implemented in two ways:</p>
<ul>
<li><strong>Pre-training of the conversational model</strong> implies fine-tuning of the existing language model on the corpus of texts, or on specially prepared question-answer pairs. In Russian, there is a family of relatively small models <a href="https://huggingface.co/ai-forever">ruGPT</a>, which can be fine-tuned on one A100 GPU. Also, as of lately, Yandex GPT cloud service also supports fine-tuning. In any case, finetuning requires considerable computing power, effort and experience, and at the same time any changes in the subject area require re-training of the model. Imagine a situation when we implemented a consultant for a bank in this way, and then the interest rate on deposits changed - this fact cannot be easily integrated into the model without re-training.</li>
<li><strong>Retrieval-Augmented Generation</strong> is an approach in which the chatbot’s response is formed by a standard pre-trained LLM model, but during answering this model is shown fragments of text from a domain-oriented knowledge base found using semantic search. In this case, LLM is used in the advanced paraphraser mode and extracting the answer to the question from the text. This approach is essentially similar to the previously popular <a href="/azure/deep-pavlov-answers-covid-questions-ru/">Open Domain Question Answering</a>, used in conjunction with BERT-type models.</li>
</ul>
<p>In this article, we will consider creating a question-and-answer chatbot using the latter approach using the <a href="https://www.langchain.com/">LangChain framework</a> and the <a href="https://cloud.yandex.ru/services/yandexgpt">Yandex GPT</a> language model. As a source material for creating a chatbot, we will use a set of video files - this will also allow us to demonstrate asynchronous speech recognition based on <a href="https://cloud.yandex.ru/services/speechkit">Yandex SpeechKit</a> to convert a video’s audio track into a text corpus.</p>
<blockquote>
<p>This article is a description of the master class held at the <a href="https://pmlconf.yandex.ru/">Practical ML Conf</a>. The entire code of the master class is available <a href="http://github.com/yandex-datasphere/VideoQABot">on GitHub</a>.</p>
</blockquote>
<p>The steps described in this article are best performed using <a href="https://cloud.yandex.ru/docs/datasphere/">Yandex DataSphere</a>, because it provides convenient integration with other Yandex Cloud services, for example, S3 object storage (which, in turn, is needed for asynchronous speech recognition). However, you can also use other tools.</p>
<h2 id="how-retrieval-augmented-generation-works">How Retrieval-Augmented Generation Works</h2>
<p>Imagine that we want to use a large language model as an assistant or a smart chatbot. In the simplest case, to get more or less consistent and appropriate style responses, we use <em>Prompt Engineering</em>, i.e. modify the original question, or precede it with a set of specific instructions, for example:</p>
<pre>
Imagine that you are an assistant in an electronics store
named Vasya, and you need to answer customer requests
about various models of equipment. Answer the question below in
as much detail as possible:
[question]
How is iPhone better than Android?
[/question]
</pre>
<p>As a diagram, this can be represented in the following way:</p>
<p><img src="/images/blog/YaGPT/simple-query-en.png" alt="Simple Querying" /></p>
<p>In the case of Retrieval-Augmented Generation, we have some knowledge base consisting of small but meaningful fragments of text - usually about 1024 tokens. Based on the request received from the user, we search for the most relevant text fragments - for example, 3 or 5 of the most relevant ones - and then ask the language model to answer the question by looking at the text fragments found:</p>
<pre>
Imagine that you are an assistant in an electronics store
named Vasya, and you need to answer customer requests
about various models of equipment. Read the text in the info tags and
answer the question in the question tags in as much detail as possible. If
the explicit answer is not contained in the text, do not try to come up with it.
[info]
The leading electronics publication writes that the iPhone is ahead of its
competitors in terms of camera quality. In addition, ...
[/info]
[question]
How is iPhone better than Android?
[/question]
</pre>
<p>This process is shown in this diagram below:</p>
<p><img src="/images/blog/YaGPT/retrieval-augmented-query-en.png" alt="Retrieval-Augmented Generation" /></p>
<p>However, we need to organize smart search through a collection of documents, which is better than just a full-text search, and takes into account the meaning. To do this, the concept of <strong>text embeddings</strong> is used - a way to form a certain <em>semantic vector</em> from a text fragment in such a way that for text fragments that are close in meaning, the vectors will also be close in the sense of some metric.</p>
<p>Thus, we will need to calculate embeddings for all text fragments (this can be done once during the initial indexing), and then for the query, and find the nearest vectors by distance - the closest text fragments will correspond to them.</p>
<p>To store vectors and quickly search through them, special <strong>vector databases</strong> are used. Thus, a specialized question-and-answer chatbot will include embedding calculation, a vector database of content, a large language model and some prompt engineering. All these components are conveniently contained in the <a href="https://www.langchain.com">LangChain library</a>, which has been rapidly gaining popularity lately.</p>
<p>Below I will tell you how to build a question-answer bot on LangChain based on a set of video files.</p>
<h2 id="convert-video-to-text">Convert Video to Text</h2>
<p>To begin with, we need to assemble a text corpus containing information from the subject area of interest. As source data, we will take several videos from YouTube, for example, reviews of various consumer equipment from popular blogger <a href="https://wylsa.com/">Wylsacom</a>.</p>
<p><img src="/images/blog/YaGPT/wylsacom.jpg" alt="Wylsacom" /></p>
<p>It will be enough for us to collect links to the video:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">videos</span> <span class="o">=</span> <span class="p">[</span><span class="s">'https://www.youtube.com/watch?v=QuSz0FAvNrE'</span><span class="p">,</span>
<span class="c1"># there may be other videos here
</span> <span class="s">'https://www.youtube.com/watch?v=3ucnBEkVuKc'</span>
<span class="p">]</span>
</code></pre></div></div>
<p>To download audio tracks for these videos, we use <a href="https://pytube.io/">pytube</a> library:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">i</span><span class="p">,</span><span class="n">url</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">videos</span><span class="p">):</span>
<span class="n">yt</span> <span class="o">=</span> <span class="n">YouTube</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Downloading </span><span class="si">{</span><span class="n">yt</span><span class="p">.</span><span class="n">title</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">yt</span><span class="p">.</span><span class="n">streams</span><span class="p">.</span><span class="nb">filter</span><span class="p">(</span><span class="n">mime_type</span><span class="o">=</span><span class="s">"audio/webm"</span><span class="p">).</span><span class="n">first</span><span class="p">().</span><span class="n">download</span><span class="p">(</span>
<span class="n">output_path</span><span class="o">=</span><span class="s">"./audio"</span><span class="p">,</span><span class="n">filename</span><span class="o">=</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">.opus"</span><span class="p">)</span>
</code></pre></div></div>
<p>As a result, we will have numbered audio files in opus format in the <code class="language-plaintext highlighter-rouge">audio</code> directory.</p>
<p>Before starting recognition, you need to convert those files to a format that <a href="https://cloud.yandex.ru/docs/speechkit/formats">Yandex SpeechKit</a> will understand. To do this, let’s use the <code class="language-plaintext highlighter-rouge">librosa</code> library:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">glob</span>
<span class="kn">import</span> <span class="nn">librosa</span>
<span class="kn">import</span> <span class="nn">soundfile</span> <span class="k">as</span> <span class="n">sf</span>
<span class="n">target_sr</span> <span class="o">=</span> <span class="mi">8000</span>
<span class="k">for</span> <span class="n">fn</span> <span class="ow">in</span> <span class="n">glob</span><span class="p">.</span><span class="n">glob</span><span class="p">(</span><span class="s">"./audio/*.opus"</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Processing </span><span class="si">{</span><span class="n">fn</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">au</span><span class="p">,</span><span class="n">sr</span> <span class="o">=</span> <span class="n">librosa</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span><span class="n">sr</span><span class="o">=</span><span class="n">target_sr</span><span class="p">)</span>
<span class="n">sf</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">fn</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'.opus'</span><span class="p">,</span><span class="s">'.ogg'</span><span class="p">),</span><span class="n">au</span><span class="p">,</span>
<span class="n">target_sr</span><span class="p">,</span><span class="nb">format</span><span class="o">=</span><span class="s">'ogg'</span><span class="p">,</span><span class="n">subtype</span><span class="o">=</span><span class="s">'opus'</span><span class="p">)</span>
</code></pre></div></div>
<p>As a result, we get a set of files with the extension <code class="language-plaintext highlighter-rouge">ogg</code>, which can be submitted to Yandex Speechkit. Since we are talking about recognizing a large volume of text, we will use <a href="https://cloud.yandex.ru/docs/speechkit/stt/transcribation">asynchronous recognition</a> (<em>transcription</em>): to do this, we need to put all the files in S3 storage, start the recognition process, and then periodically check the results.</p>
<p>When using DataSphere, the easiest way to copy files to S3 is by connecting some storage to the DataSphere via the S3 connector. Suppose we have mounted the <code class="language-plaintext highlighter-rouge">mclass</code> bucket to the <code class="language-plaintext highlighter-rouge">mclass</code> directory, in this case the files can be moved by simple copying:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">!</span><span class="n">mkdir</span> <span class="o">-</span><span class="n">p</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">jupyter</span><span class="o">/</span><span class="n">mnt</span><span class="o">/</span><span class="n">s3</span><span class="o">/</span><span class="n">mclass</span><span class="o">/</span><span class="n">audio</span>
<span class="err">!</span><span class="n">cp</span> <span class="p">.</span><span class="o">/</span><span class="n">audio</span><span class="o">/*</span><span class="p">.</span><span class="n">ogg</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">jupyter</span><span class="o">/</span><span class="n">mnt</span><span class="o">/</span><span class="n">s3</span><span class="o">/</span><span class="n">mclass</span><span class="o">/</span><span class="n">audio</span>
</code></pre></div></div>
<p>To start recognition, we describe the function <code class="language-plaintext highlighter-rouge">submit_for_sr</code>, which will form a request in accordance with <a href="https://cloud.yandex.ru/docs/speechkit/stt/api/transcribation-api">this API</a>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">submit_for_sr</span><span class="p">(</span><span class="n">audio_file</span><span class="p">):</span>
<span class="n">j</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"config"</span><span class="p">:</span> <span class="p">{</span>
<span class="s">"specification"</span><span class="p">:</span> <span class="p">{</span> <span class="s">"languageCode"</span><span class="p">:</span> <span class="s">"ru-RU"</span> <span class="p">}},</span>
<span class="s">"audio"</span><span class="p">:</span> <span class="p">{</span> <span class="s">"uri"</span><span class="p">:</span> <span class="n">audio_file</span> <span class="p">}}</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span>
<span class="s">"https://transcribe.api.cloud.yandex.net/speech/stt/v2/longRunningRecognize"</span><span class="p">,</span>
<span class="n">json</span> <span class="o">=</span> <span class="n">j</span><span class="p">,</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"Authorization"</span> <span class="p">:</span> <span class="sa">f</span><span class="s">"Api-Key </span><span class="si">{</span><span class="n">api_key</span><span class="si">}</span><span class="s">"</span> <span class="p">})</span>
<span class="k">return</span> <span class="n">res</span><span class="p">.</span><span class="n">json</span><span class="p">()[</span><span class="s">'id'</span><span class="p">]</span>
</code></pre></div></div>
<p>At the same time, for this function to work, we need to create a <a href="https://cloud.yandex.ru/docs/iam/concepts/users/service-accounts">service account</a> in our cloud, which has access to the speech recognition function, reading the S3 storage and working with language models. We also need to create an <a href="https://cloud.yandex.ru/docs/iam/operations/api-key/create">API key</a> for this account. It is assumed that the <code class="language-plaintext highlighter-rouge">api_key</code> variable contains this key.</p>
<p>To send all files for recognition, we use the simple loop:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">d</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">fn</span> <span class="ow">in</span> <span class="n">glob</span><span class="p">.</span><span class="n">glob</span><span class="p">(</span><span class="s">'/home/jupyter/mnt/s3/mclass/audio/*.ogg'</span><span class="p">):</span>
<span class="n">ext_name</span> <span class="o">=</span> <span class="n">fn</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'/home/jupyter/mnt/s3/'</span><span class="p">,</span>
<span class="s">'https://storage.yandexcloud.net/'</span><span class="p">)</span>
<span class="nb">id</span> <span class="o">=</span> <span class="n">submit_for_sr</span><span class="p">(</span><span class="n">ext_name</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Submitted </span><span class="si">{</span><span class="n">fn</span><span class="si">}</span><span class="s"> -> </span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">d</span><span class="p">[</span><span class="nb">id</span><span class="p">]</span> <span class="o">=</span> <span class="n">fn</span>
</code></pre></div></div>
<p>In this code, using <code class="language-plaintext highlighter-rouge">replace</code>, we replace the local path to the file with a URL link to the file in S3 storage. At the same time, the storage does not have to be open for reading - the necessary access will be automatically granted to the corresponding service account.</p>
<p>As a result, the dictionary <code class="language-plaintext highlighter-rouge">d</code> will contain lists of identifiers of recognition processes, and the corresponding file paths. To check the readiness of recognition, we define a function:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">check_ready</span><span class="p">(</span><span class="nb">id</span><span class="p">):</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"https://operation.api.cloud.yandex.net/operations/</span><span class="si">{</span><span class="nb">id</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"Authorization"</span> <span class="p">:</span> <span class="sa">f</span><span class="s">"Api-Key </span><span class="si">{</span><span class="n">api_key</span><span class="si">}</span><span class="s">"</span> <span class="p">})</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">json</span><span class="p">()</span>
<span class="k">if</span> <span class="n">res</span><span class="p">[</span><span class="s">'done'</span><span class="p">]:</span>
<span class="k">return</span> <span class="n">res</span><span class="p">[</span><span class="s">'response'</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">None</span>
</code></pre></div></div>
<p>While the result is not ready, this function will return <code class="language-plaintext highlighter-rouge">None</code>, and when ready, it will return a JSON file with recognized fragments.</p>
<p>The code below checks all recognition processes for readiness, and if ready, puts the result in the <code class="language-plaintext highlighter-rouge">txt</code> dictionary:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">txt</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">for</span> <span class="n">k</span><span class="p">,</span><span class="n">v</span> <span class="ow">in</span> <span class="n">d</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">txt</span><span class="p">.</span><span class="n">keys</span><span class="p">():</span>
<span class="k">continue</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">check_ready</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
<span class="k">if</span> <span class="n">res</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s"> -> waiting"</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s"> -> ready"</span><span class="p">)</span>
<span class="n">txt</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="o">=</span> <span class="s">' '</span><span class="p">.</span><span class="n">join</span><span class="p">([</span><span class="n">x</span><span class="p">[</span><span class="s">'alternatives'</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s">'text'</span><span class="p">]</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">res</span><span class="p">[</span><span class="s">'chunks'</span><span class="p">]])</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">txt</span><span class="p">.</span><span class="n">keys</span><span class="p">())</span><span class="o">==</span><span class="nb">len</span><span class="p">(</span><span class="n">d</span><span class="p">.</span><span class="n">keys</span><span class="p">()):</span>
<span class="k">break</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
</code></pre></div></div>
<p>When all the results are received, we just need to save the text files:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">k</span><span class="p">,</span><span class="n">v</span> <span class="ow">in</span> <span class="n">txt</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">k</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'.ogg'</span><span class="p">,</span><span class="s">'.txt'</span><span class="p">)</span>
<span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'/audio/'</span><span class="p">,</span><span class="s">'/text/'</span><span class="p">),</span>
<span class="s">'w'</span><span class="p">,</span><span class="n">encoding</span><span class="o">=</span><span class="s">'utf-8'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="breaking-the-text-into-fragments">Breaking the Text into Fragments</h2>
<p>At the previous stage, we obtained a set of text files, one per video. However, they are most likely too large to be used for queries. The fact is that we have two limitations:</p>
<ul>
<li><strong>The size of the embedding context</strong> limits how many tokens we can use to calculate the semantic vector. Usually, the size of the embedding context is not too large - from 512 tokens to 2048.</li>
</ul>
<blockquote>
<p>A token is a unit of input text that is fed to the input of a neural network model. Usually, a token is a word, or more often a part of a word. For example, in the Yandex GPT model, the length of one token is usually about 3-4 characters.</p>
</blockquote>
<ul>
<li><strong>The size of the context of the language model</strong>, i.e. how long the request (or request+response) can be. For Yandex GPT, the length of the request+response context is just over 7000 tokens, and these tokens should include 3-5 of the best text fragments found, the user’s question itself with prompt instructions, and the response given to the user.</li>
</ul>
<p>Based on these considerations, the length of the text fragment is usually chosen to be 512-2048 tokens. Sometimes it is easier to set this length in characters, because it is not always obvious in advance how the text will be tokenized.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">langchain</span>
<span class="kn">import</span> <span class="nn">langchain.document_loaders</span>
<span class="n">source_dir</span> <span class="o">=</span> <span class="s">"/home/jupyter/mnt/s3/mclass/text"</span>
<span class="n">loader</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">document_loaders</span><span class="p">.</span><span class="n">DirectoryLoader</span><span class="p">(</span>
<span class="n">source_dir</span><span class="p">,</span><span class="n">glob</span><span class="o">=</span><span class="s">"*.txt"</span><span class="p">,</span>
<span class="n">show_progress</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span><span class="n">recursive</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">splitter</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">text_splitter</span><span class="p">.</span><span class="n">RecursiveCharacterTextSplitter</span><span class="p">(</span>
<span class="n">chunk_size</span><span class="o">=</span><span class="mi">1024</span><span class="p">,</span><span class="n">chunk_overlap</span><span class="o">=</span><span class="mi">128</span><span class="p">)</span>
<span class="n">fragments</span> <span class="o">=</span> <span class="n">splitter</span><span class="p">.</span><span class="n">create_documents</span><span class="p">(</span>
<span class="p">[</span> <span class="n">x</span><span class="p">.</span><span class="n">page_content</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">loader</span><span class="p">.</span><span class="n">load</span><span class="p">()</span> <span class="p">])</span>
</code></pre></div></div>
<p>In this case, we use a convenient class <code class="language-plaintext highlighter-rouge">RecursiveCharacterTextSplitter</code>, which first tries to split the text by large separators (paragraphs), then by separators between sentences, and in the worst case uses separators between words. This allows us to get the most meaningful text fragments.</p>
<p>As a result, we will get a variable <code class="language-plaintext highlighter-rouge">fragments</code> containing fragments of text.</p>
<blockquote>
<p>In real projects, when indexing a large volume of documents, you should not expect that they will all fit into memory. In this case, splitting into fragments must be combined with placing fragments in a vector database that is stored on disk.</p>
</blockquote>
<h2 id="calculating-embeddings">Calculating Embeddings</h2>
<p>Calculating embeddings is quite an important task, and it is not easy to choose the best option for the Russian language. LangChain contains many ready-made classes that allow you to calculate embeddings both locally using pre-trained (or even self-trained) models, as well as using online services such as OpenAI.</p>
<p>I will focus on two options for calculating embeddings:</p>
<ul>
<li>Use some model from HuggingFace with Russian language support. LangChain allows you to calculate embeddings using HuggingFace models in a couple of lines of code. The size of the context of such a model is usually not very large, so you will need to appropriately select <code class="language-plaintext highlighter-rouge">chunk_size</code> in the code above when splitting the text</li>
</ul>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">embeddings</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">embeddings</span><span class="p">.</span><span class="n">HuggingFaceEmbedding</span><span class="p">(</span>
<span class="n">model_name</span><span class="o">=</span><span class="s">"distiluse-base-multilingual-cased-v1"</span><span class="p">)</span>
<span class="n">sample_vec</span> <span class="o">=</span> <span class="n">embeddings</span><span class="p">.</span><span class="n">embed_query</span><span class="p">(</span><span class="s">"Hello, world!"</span><span class="p">)</span>
</code></pre></div></div>
<ul>
<li>Use <a href="https://cloud.yandex.ru/docs/yandexgpt/api-ref/Embeddings/">Yandex GPT embedding calculation service</a>. In this case, we will need to implement the adapter ourselves to calculate embeddings in LangChain, inheriting it from <code class="language-plaintext highlighter-rouge">langchain.embeddings.base.Embeddings</code>:</li>
</ul>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">langchain.embeddings.base</span> <span class="kn">import</span> <span class="n">Embeddings</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">class</span> <span class="nc">YaGPTEmbeddings</span><span class="p">(</span><span class="n">Embeddings</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span><span class="n">folder_id</span><span class="p">,</span><span class="n">api_key</span><span class="p">,</span><span class="n">sleep_interval</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
<span class="bp">self</span><span class="p">.</span><span class="n">folder_id</span> <span class="o">=</span> <span class="n">folder_id</span>
<span class="bp">self</span><span class="p">.</span><span class="n">api_key</span> <span class="o">=</span> <span class="n">api_key</span>
<span class="bp">self</span><span class="p">.</span><span class="n">sleep_interval</span> <span class="o">=</span> <span class="n">sleep_interval</span>
<span class="bp">self</span><span class="p">.</span><span class="n">headers</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"Authorization"</span> <span class="p">:</span> <span class="sa">f</span><span class="s">"Api-key </span><span class="si">{</span><span class="n">api_key</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
<span class="s">"x-folder-id"</span> <span class="p">:</span> <span class="n">folder_id</span> <span class="p">}</span>
<span class="k">def</span> <span class="nf">embed_document</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">text</span><span class="p">):</span>
<span class="n">j</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"model"</span> <span class="p">:</span> <span class="s">"general:embedding"</span><span class="p">,</span>
<span class="s">"embedding_type"</span> <span class="p">:</span> <span class="s">"EMBEDDING_TYPE_DOCUMENT"</span><span class="p">,</span>
<span class="s">"text"</span><span class="p">:</span> <span class="n">text</span>
<span class="p">}</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span>
<span class="s">"https://llm.api.cloud.yandex.net/llm/v1alpha/embedding"</span><span class="p">,</span>
<span class="n">json</span><span class="o">=</span><span class="n">j</span><span class="p">,</span><span class="n">headers</span><span class="o">=</span><span class="bp">self</span><span class="p">.</span><span class="n">headers</span><span class="p">)</span>
<span class="n">vec</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">json</span><span class="p">()[</span><span class="s">'embedding'</span><span class="p">]</span>
<span class="k">return</span> <span class="n">vec</span>
<span class="k">def</span> <span class="nf">embed_documents</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">texts</span><span class="p">,</span> <span class="n">chunk_size</span> <span class="o">=</span> <span class="mi">0</span><span class="p">):</span>
<span class="n">res</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">texts</span><span class="p">:</span>
<span class="n">res</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">embed_document</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">sleep_interval</span><span class="p">)</span>
<span class="k">return</span> <span class="n">res</span>
<span class="k">def</span> <span class="nf">embed_query</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">text</span><span class="p">):</span>
<span class="n">j</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"model"</span> <span class="p">:</span> <span class="s">"general:embedding"</span><span class="p">,</span>
<span class="s">"embedding_type"</span> <span class="p">:</span> <span class="s">"EMBEDDING_TYPE_QUERY"</span><span class="p">,</span>
<span class="s">"text"</span><span class="p">:</span> <span class="n">text</span>
<span class="p">}</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span>
<span class="s">"https://llm.api.cloud.yandex.net/llm/v1alpha/embedding"</span><span class="p">,</span>
<span class="n">json</span><span class="o">=</span><span class="n">j</span><span class="p">,</span><span class="n">headers</span><span class="o">=</span><span class="bp">self</span><span class="p">.</span><span class="n">headers</span><span class="p">)</span>
<span class="n">vec</span> <span class="o">=</span> <span class="n">res</span><span class="p">.</span><span class="n">json</span><span class="p">()[</span><span class="s">'embedding'</span><span class="p">]</span>
<span class="k">return</span> <span class="n">vec</span>
<span class="n">embeddings</span> <span class="o">=</span> <span class="n">YaGPTEmbeddings</span><span class="p">(</span><span class="n">folder_id</span><span class="p">,</span><span class="n">api_key</span><span class="p">)</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">embeddings</span><span class="p">.</span><span class="n">embed_documents</span><span class="p">([</span><span class="s">'Hello'</span><span class="p">,</span><span class="s">'there'</span><span class="p">])</span>
</code></pre></div></div>
<blockquote>
<p><strong>Update Oct 2023</strong>: Nowadays, instead of implementing embeddings class yourself, you can use <a href="https://github.com/yandex-datasphere/yandex-chain">yandex-chain</a> library, which contains more robust implementation.</p>
</blockquote>
<p>This class contains two main methods, each of which calls the corresponding Yandex Cloud API:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">embed_query</code> is used to calculate the embedding of the user’s request</li>
<li><code class="language-plaintext highlighter-rouge">embed_documents</code> is used to calculate the embedding of a document sequence. Since the Yandex API supports embedding calculation for only one document per call, this method is implemented as a loop calling the <code class="language-plaintext highlighter-rouge">embed_document</code> method for each document in the collection.</li>
</ul>
<blockquote>
<p>Since access to the service is currently limited to 1 request per second, a delay has been added between calls.</p>
</blockquote>
<p>In the next section, we will need to calculate embeddings. To do this, we use the variable <code class="language-plaintext highlighter-rouge">embeddings</code> - you can use one of the two options offered above to choose from.</p>
<h2 id="saving-documents-to-a-vector-database">Saving Documents to a Vector Database</h2>
<p>LangChain supports many vector databases, from the very simple and lightweight <a href="https://github.com/chroma-core/chroma">ChromaDB</a>, up to a large cluster solution [OpenSearch] (https://opensearch.org/). You can choose a solution based on the complexity of the task and the amount of data.</p>
<blockquote>
<p>If you are building a solution in Yandex Cloud, then you can use <a href="https://cloud.yandex.ru/services/managed-opensearch">managed OpenSearch in Yandex Cloud</a> - this will simplify management, and will allow you to leave scaling to cloud services.</p>
</blockquote>
<p>In our example, we use <a href="https://lancedb.com/">LanceDB</a>, because it allows you to save the database in a regular directory. First, let’s create a table:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">langchain.vectorstores</span> <span class="kn">import</span> <span class="n">LanceDB</span>
<span class="kn">import</span> <span class="nn">lancedb</span>
<span class="n">db_dir</span> <span class="o">=</span> <span class="s">"../store"</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">lancedb</span><span class="p">.</span><span class="n">connect</span><span class="p">(</span><span class="n">db_dir</span><span class="p">)</span>
<span class="n">table</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">create_table</span><span class="p">(</span>
<span class="s">"vector_index"</span><span class="p">,</span>
<span class="n">data</span><span class="o">=</span><span class="p">[{</span>
<span class="s">"vector"</span><span class="p">:</span> <span class="n">embeddings</span><span class="p">.</span><span class="n">embed_query</span><span class="p">(</span><span class="s">"Hello World"</span><span class="p">),</span>
<span class="s">"text"</span><span class="p">:</span> <span class="s">"Hello World"</span><span class="p">,</span>
<span class="s">"id"</span><span class="p">:</span> <span class="s">"1"</span><span class="p">,</span>
<span class="p">}],</span>
<span class="n">mode</span><span class="o">=</span><span class="s">"overwrite"</span><span class="p">)</span>
</code></pre></div></div>
<p>To index all documents, we use the capabilities of LangChain:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">db</span> <span class="o">=</span> <span class="n">LanceDB</span><span class="p">.</span><span class="n">from_documents</span><span class="p">(</span><span class="n">fragments</span><span class="p">,</span> <span class="n">embeddings</span><span class="p">,</span> <span class="n">connection</span><span class="o">=</span><span class="n">table</span><span class="p">)</span>
</code></pre></div></div>
<p>Now, to find the closest documents by distance, you can use the <code class="language-plaintext highlighter-rouge">similarity_search</code> method:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">q</span><span class="o">=</span><span class="s">"How is iPhone better than Samsung?"</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">similarity_search</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">res</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'-'</span><span class="o">*</span><span class="mi">40</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">page_content</span><span class="p">)</span>
</code></pre></div></div>
<p>You can also use the <code class="language-plaintext highlighter-rouge">retriever</code> interface, which allows you to set various search strategies and parameters, for example:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">retriever</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">as_retriever</span><span class="p">(</span>
<span class="n">search_kwargs</span><span class="o">=</span><span class="p">{</span><span class="s">"k"</span><span class="p">:</span> <span class="mi">5</span><span class="p">})</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">retriever</span><span class="p">.</span><span class="n">get_relevant_documents</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="working-with-yandex-gpt">Working with Yandex GPT</h2>
<p>Just as in the case of embeddings, LangChain does not contain built-in tools for working with the Yandex GPT generative language model. Therefore, we need to implement the adapter ourselves, according to <a href="https://python.langchain.com/docs/modules/model_io/models/llms/custom_llm">the documentation</a>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">YandexLLM</span><span class="p">(</span><span class="n">langchain</span><span class="p">.</span><span class="n">llms</span><span class="p">.</span><span class="n">base</span><span class="p">.</span><span class="n">LLM</span><span class="p">):</span>
<span class="n">api_key</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="bp">None</span>
<span class="n">folder_id</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">max_tokens</span> <span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1500</span>
<span class="n">temperature</span> <span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">instruction_text</span> <span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">def</span> <span class="nf">_call</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prompt</span><span class="p">)</span> <span class="p">:</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"x-folder-id"</span> <span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">folder_id</span> <span class="p">,</span>
<span class="s">"Authorization"</span> <span class="p">:</span> <span class="sa">f</span><span class="s">"Api-key </span><span class="si">{</span><span class="bp">self</span><span class="p">.</span><span class="n">api_key</span><span class="si">}</span><span class="s">"</span><span class="p">}</span>
<span class="n">req</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"model"</span><span class="p">:</span> <span class="s">"general"</span><span class="p">,</span>
<span class="s">"instruction_text"</span><span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">instruction_text</span><span class="p">,</span>
<span class="s">"request_text"</span><span class="p">:</span> <span class="n">prompt</span><span class="p">,</span>
<span class="s">"generation_options"</span><span class="p">:</span> <span class="p">{</span>
<span class="s">"max_tokens"</span><span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">max_tokens</span><span class="p">,</span>
<span class="s">"temperature"</span><span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">temperature</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span>
<span class="s">"https://llm.api.cloud.yandex.net/llm/v1alpha/instruct"</span><span class="p">,</span>
<span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">req</span><span class="p">).</span><span class="n">json</span><span class="p">()</span>
<span class="k">return</span> <span class="n">res</span><span class="p">[</span><span class="s">'result'</span><span class="p">][</span><span class="s">'alternatives'</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s">'text'</span><span class="p">]</span>
</code></pre></div></div>
<p>This code is slightly simplified for clarity, a more complete version is contained <a href="https://github.com/yandex-datasphere/VideoQABot/blob/main/YaGPT.py">in the GitHub repository</a></p>
<blockquote>
<p><strong>Update Oct 2023</strong>: Nowadays, instead of implementing this class yourself, you can use <a href="https://github.com/yandex-datasphere/yandex-chain">yandex-chain</a> library, which contains more robust implementation. Also, Yandex GPT is supported in <a href="https://github.com/ai-forever/gigachain">GigaChain</a> library, a LangChain clone supported by Sber.</p>
</blockquote>
<p>Now we can work with the model as follows:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">instructions</span> <span class="o">=</span> <span class="s">"""
Imagine that you are a tech blogger who reviews
modern electronics. A friend is asking for you. Try
to answer his question in detail and in an accessible way."""</span>
<span class="n">llm</span> <span class="o">=</span> <span class="n">YandexLLM</span><span class="p">(</span><span class="n">api_key</span><span class="o">=</span><span class="n">api_key</span><span class="p">,</span> <span class="n">folder_id</span><span class="o">=</span><span class="n">folder_id</span><span class="p">,</span>
<span class="n">instruction_text</span> <span class="o">=</span> <span class="n">instructions</span><span class="p">)</span>
<span class="n">llm</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
</code></pre></div></div>
<p>If we ask a question about how the iPhone is better than Samsung, we can get something like this answer:</p>
<p><em>
Greetings! Today I want to compare two popular smartphones - iPhone and Samsung. Both options have their advantages and disadvantages.<br />
Let’s start with the design. Unlike many Samsung called “bricks”, the combination of iPhone materials creates a sense of minimalism and elegance. In addition, the iPhone is dedicated to monobrow, the Raise to wake function, so that you can enjoy full-screen viewing of videos or images without black caps during operation, is convenient to use, is universal (all known applications work)<br />
The Samsung Galaxy design has a highly-concentrated Document Center Wheel and a Trade application for front control, which is conveniently used to determine brightness, open a multitasking application. It also has a built-in camera with lots of picture effects that are fun for you. To these changes, you can add a stepper sensor.
</em></p>
<p>This text has been translated from Russian for clarity.</p>
<h2 id="implementing-retrieval-augmented-generation">Implementing Retrieval-Augmented Generation</h2>
<p>In the code above, we have already found the 5 most suitable text fragments for our query <code class="language-plaintext highlighter-rouge">q</code>, and they are in the <code class="language-plaintext highlighter-rouge">res</code> variable. Each document has a <code class="language-plaintext highlighter-rouge">page_content</code> field with the page text.</p>
<p>Retrieval-Augmented Generation can be implemented using the chain mechanism and the <code class="language-plaintext highlighter-rouge">StuffDocumentsChain</code> method, which does the following:</p>
<ol>
<li>Takes a collection of <code class="language-plaintext highlighter-rouge">input_documents</code></li>
<li>Each of them passes through some <code class="language-plaintext highlighter-rouge">document_prompt</code> template, and then results are combined together.</li>
<li>Resulting text is placed in the variable <code class="language-plaintext highlighter-rouge">document_variable_name</code> and passed to the large language model <code class="language-plaintext highlighter-rouge">llm</code></li>
</ol>
<p>In our case, <code class="language-plaintext highlighter-rouge">document_prompt</code> will not modify the document, but will simply return it unchanged:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Prompt for document
</span><span class="n">processing</span> <span class="n">document_prompt</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">prompts</span><span class="p">.</span><span class="n">PromptTemplate</span><span class="p">(</span>
<span class="n">input_variables</span><span class="o">=</span><span class="p">[</span><span class="s">"page_content"</span><span class="p">],</span> <span class="n">template</span><span class="o">=</span><span class="s">"{page_content}"</span><span class="p">)</span>
</code></pre></div></div>
<p>To form the final answer, we use a more complex template that accepts a user query <code class="language-plaintext highlighter-rouge">query</code> and a context <code class="language-plaintext highlighter-rouge">context</code> (these are just the most relevant text fragments found):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Prompt for the language model
</span><span class="n">document_variable_name</span> <span class="o">=</span> <span class="s">"context"</span>
<span class="n">template</span> <span class="o">=</span> <span class="s">"""
Please look at the text below and answer the question using
the information from this text.
Text:
-----
{context}
-----
Question:
{query}"""</span>
<span class="n">prompt</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">prompts</span><span class="p">.</span><span class="n">PromptTemplate</span><span class="p">(</span>
<span class="n">template</span><span class="o">=</span><span class="n">template</span><span class="p">,</span>
<span class="n">input_variables</span><span class="o">=</span><span class="p">[</span><span class="s">"context"</span><span class="p">,</span> <span class="s">"query"</span><span class="p">])</span>
</code></pre></div></div>
<p>Next, we create the <code class="language-plaintext highlighter-rouge">llm_chain</code> chain, which calls the Yandex GPT <code class="language-plaintext highlighter-rouge">llm</code> language model described earlier with the <code class="language-plaintext highlighter-rouge">prompt</code> template, after which we initialize the main <code class="language-plaintext highlighter-rouge">chain</code>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Creating a chain
</span><span class="n">llm_chain</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">chains</span><span class="p">.</span><span class="n">LLMChain</span><span class="p">(</span><span class="n">llm</span><span class="o">=</span><span class="n">llm</span><span class="p">,</span> <span class="n">prompt</span><span class="o">=</span><span class="n">prompt</span><span class="p">)</span>
<span class="n">chain</span> <span class="o">=</span> <span class="n">langchain</span><span class="p">.</span><span class="n">chains</span><span class="p">.</span><span class="n">StuffDocumentsChain</span><span class="p">(</span>
<span class="n">llm_chain</span><span class="o">=</span><span class="n">llm_chain</span><span class="p">,</span>
<span class="n">document_prompt</span><span class="o">=</span><span class="n">document_prompt</span><span class="p">,</span>
<span class="n">document_variable_name</span><span class="o">=</span><span class="n">document_variable_name</span><span class="p">)</span>
</code></pre></div></div>
<p>Now, to get a response to the <code class="language-plaintext highlighter-rouge">q</code> query with the <code class="language-plaintext highlighter-rouge">res</code> document collection, we simply run the chain:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">chain</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">input_documents</span><span class="o">=</span><span class="n">res</span><span class="p">,</span> <span class="n">query</span><span class="o">=</span><span class="n">q</span><span class="p">)</span>
</code></pre></div></div>
<p>Here is the answer received in our case:</p>
<p><em>
If you want to sell your smartphone after a long time in the future, I would recommend investing in Apple, because after the recent release of the new iphone SE - they are still able to provide a decent cost of devices over time. In addition, apple smartphones show greater stability in the main systems and applications. Despite these limitations, the ability to upgrade devices over time can be an incentive for those looking for a long-term investment goal for their devices.<br />
If the value of the phone is a key factor, I would recommend considering models with the best LCDs in your budget to consider a long-term exchange. Samsung is still the leader in the production of displays, especially in their galaxy S line.<br />
I would also pay attention to the iOS software, which provides many additional features, although the lack of an update might confuse some users. </em></p>
<p>Subjectively, it seems that this answer is much deeper, probably due to the fact that it takes into account the expert comments made by Wylsacom.</p>
<h2 id="context-transformation">Context Transformation</h2>
<p>Now it remains for us to put everything together and implement one <code class="language-plaintext highlighter-rouge">answer</code> function that answers the user’s question.</p>
<p>However, we will also add one more small final touch - this is a change in the order of fragments in the context. The fact is that often language models are arranged in such a way that the most significant fragments of a query are its beginning and end, and therefore it is more logical to place more relevant text fragments closer to the beginning and end of the query. The <code class="language-plaintext highlighter-rouge">LongContextReorder</code> class allows us to easily implement such reordering of text fragments.</p>
<p>With that said, the <code class="language-plaintext highlighter-rouge">answer</code> function will look like this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">langchain.document_transformers</span> <span class="kn">import</span> <span class="n">LongContextReorder</span>
<span class="n">reorderer</span> <span class="o">=</span> <span class="n">LongContextReorder</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">answer</span><span class="p">(</span><span class="n">query</span><span class="p">,</span><span class="n">reorder</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">retriever</span><span class="p">.</span><span class="n">get_relevant_documents</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
<span class="k">if</span> <span class="n">reorder</span><span class="p">:</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">reorderer</span><span class="p">.</span><span class="n">transform_documents</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
<span class="k">return</span> <span class="n">chain</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">input_documents</span><span class="o">=</span><span class="n">results</span><span class="p">,</span> <span class="n">query</span><span class="o">=</span><span class="n">query</span><span class="p">)</span>
</code></pre></div></div>
<p>We can also implement a function to compare the responses of just the language model, and our bot with Retrieval-Augmented Generation:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">compare</span><span class="p">(</span><span class="n">q</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"YaGPT response: </span><span class="si">{</span><span class="n">llm</span><span class="p">(</span><span class="n">q</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Bot's response: </span><span class="si">{</span><span class="n">answer</span><span class="p">(</span><span class="n">q</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">compare</span> <span class="p">(</span><span class="s">"Which MacBook is better for a student to choose?"</span><span class="p">)</span>
</code></pre></div></div>
<p>Look at the results:</p>
<table>
<tbody>
<tr>
<td>Yandex GPT</td>
<td>Our bot</td>
</tr>
<tr>
<td>Choosing a Macbook for a student depends on several factors, such as budget, performance requirements and tasks to be solved at the university. It is important to take into account technical characteristics such as processor, RAM, hard disk, as well as other features such as screen size, graphics, and others. If students are looking for an inexpensive device, then the Apple Macbook Air may be a good choice. It has a lightweight body, a small screen and low power consumption, which allows you to use it on the road with ease. However, if you need a more powerful laptop for work tasks, then you should consider the Macbook Pro. It has a more productive processor and a large amount of RAM, which will allow you to perform complex tasks without delay. It is also worth considering that the screen of the Macbook Pro is larger and the resolution is higher, which makes the work more comfortable. Finally, if you need to have a laptop with the ability to expand RAM and data storage, you should pay attention to the Macbook Pro with an additional expansion slot. This provides additional performance opportunities when working with demanding applications.</td>
<td>As a technical blogger specializing in reviews of modern electronics, I will say that it is better for a student to choose between a MacBook Air 13” M1 and a MacBook Pro 13” M2. If we consider the first option, then it will be an excellent choice for students, as it has a stylish design, good build quality, a fast M1 processor, sufficient internal memory and a fast SSD drive. Also, in comparison with other laptops of the same class, it wins according to the results of benchmarks and speed tests. Of the minuses — a somewhat outdated laptop design in comparison with newer models and the lack of built-in WIFI 6. In addition, the price of this laptop is a little confusing. However, it is an ideal laptop for study and work. In addition, this laptop is lighter than its previous version — the MacBook Air on an Intel processor, which makes it a good choice for mobile use. One of the main options for such an organization of the educational process in universities is distance education. Although the quarantine situation is still the focus of many people’s attention, and schools are forced to switch to distance learning, universities have also tried to speed up the transition of their students to a remote educational system as much as possible. The average student easily acquires a special desktop or laptop on which to receive e-mail. Those who previously purchased laptops with a mouse and keyboard, including junior students, lost their laptop in order to be content with an ordinary computer. It is also a variant of the curriculum, where lectures are broadcast online by teachers. Practical classes are held using tablets and laptops with their own software.</td>
</tr>
</tbody>
</table>
<h2 id="conclusions">Conclusions</h2>
<p>In this article, we tried to solve a very frequent and complex task - creating a question-and-answer bot based on video collection. One of the problems here lies in the fact that the recognition results are not always perfect, especially in terms of punctuation and formatting (paragraphs, headings, etc.). In addition, videos contain a certain number of parasitic words, incorrectly constructed grammatical fragments, etc.</p>
<p>With this in mind, it is especially nice that the result was not bad. In the last example, we see that when choosing a laptop, specific factors are being taken into account, such as online classes in a pandemic. It is obvious that it is precisely such subtleties that distinguish the view of a real expert from the “advertising text”, which is more similar to the response of Yandex GPT.</p>
<p>Let me remind readers once again that all the code from the article can be found <a href="http://github.com/yandex-datasphere/VideoQABot">in the repository</a>.</p>
2023-09-06T00:00:00+00:00https://soshnikov.com/scienceart/smoke-on-onega-play-by-chatgpt-ru/Сердце в игре: Дым над Онегой2023-08-23T00:00:00+00:00Dmitri Soshnikovhttp://soshnikov.com/В ходе недавней поездки Beach Volley Road Show мы с Алеком Петуком проделали эксперимент по коллективному написанию искусственно-интеллектуальной человеко-машинной пьесы с помощью ChatGPT. Секретную методику такого мероприятия я раскрою чуть позже, пока же предлагаю Вашему вниманию результат - пьесу, написанную коллективно участниками поездки (около 20 человек) и ChatGPT<div class="text-center">
<a class="button alert radius" href="https://www.youtube.com/watch?v=QYrbhgP1xWI" target="_blank">Режиссерская версия</a>
<a class="button alert radius" href="https://www.youtube.com/watch?v=nrmkA6J6lnc" target="_blank">Официальная версия</a>
<a class="button alert radius" href="https://www.youtube.com/watch?v=f3FmaPgLKPg" target="_blank">Клип</a>
</div>
<p><strong>Авторы:</strong> ChatGPT 4, а также Бобриков Сергей Александрович, Бобрикова Светлана, Петук Алек, Смолянская Екатерина, Регев Йоэль, Берновик Наталья, Александров Олег, Александрова Яна, Александрова Злата, Горева Ульяна, Корябочкина Мария, Сошникова Виктория, Глаголев Алексей, Глаголева Дарья, Ракусов Павел, Крылова Анна, Комиссаренко Павел, Ветлов Дмитрий, Карасев Дмитрий, Сошников Дмитрий, Горев Вадим, Горева Светлана, Бочкин Роман (каждый в меру своего участия)</p>
<p><strong>Модераторы</strong>: <a href="http://soshnikov.com">Дмитрий Сошников</a>, <a href="https://alekpetuk.com/">Алек Петук</a></p>
<p><strong>Методика генерации</strong> создана Дмитрием Сошниковым в рамках мега-проекта <strong>Театр роботов</strong>.</p>
<p><img src="/images/blog/smoke-on-onega/intro.jpg" alt="" /></p>
<div style="float: right"><i>
Нет повести грустнее и печальней,<br />
Чем повесть о любви необычайной<br />
Где двух спортсменок из первопрестольной<br />
Любовь застала на площадке волейбольной<br />
Сквозь травмы и предательство прошли они<br />
А эту пьесу написал ИИ
</i></div>
<p><br clear="all" /></p>
<h2 id="действующие-лица">Действующие лица:</h2>
<ul>
<li><strong>Даша</strong>, первая волейболистка</li>
<li><strong>Аня</strong>, вторая волейболистка</li>
<li><strong>Маруся</strong>, третья волейболистка</li>
<li><strong>Злата</strong>, четвертая волейболистка</li>
<li><strong>Леша</strong>, судья</li>
<li><strong>Царь Онега</strong></li>
<li><strong>Олег</strong>, тренер команда Даши и Ани</li>
<li><strong>Катя</strong>, ровняльщица</li>
<li><strong>Светлана</strong>, врач</li>
<li><strong>Йоэль</strong>, богатый папа Златы</li>
</ul>
<p><img src="/images/blog/smoke-on-onega/femalevolleyball.jpg" alt="" /></p>
<h2 id="сцена-1-открытие-чемпионата">Сцена 1: Открытие чемпионата</h2>
<p><em>Фон - волейбольное поле. Зрители аплодируют, свистят, музыка играет в фоне.</em></p>
<p><strong>Царь Онега</strong>: Добро пожаловать на Онежский чемпионат по волейболу “Дым над Онегой”, названный в честь известной песни “Smoke on the Water”. (огонь, дым на заднем плане) Сегодня перед нами две команды, готовые бороться за звание лучших!</p>
<p><strong>Аня</strong>: <em>(шепчет Даше)</em> Надеюсь, этот турнир станет для нас удачным.</p>
<p><strong>Даша</strong>: Конечно, мы готовы!</p>
<p><strong>Царь Онега</strong>: Первая команда, чемпионки прошлого сезона - Даша и Аня!</p>
<p><em>Зрители аплодируют.</em></p>
<p><strong>Царь Онега</strong>: И их соперники, новые звезды волейбола - Маруся и Злата!</p>
<p><em>Маруся и Злата улыбаются и машут зрителям.</em></p>
<p><strong>Злата</strong> <em>(шепчет Марусе)</em>: Готова победить?</p>
<p><strong>Маруся</strong>: Более чем!</p>
<p><strong>Светлана</strong> <em>(врач, смотря из-за кулис)</em>: Все так волнуются. Надеюсь, никто не получит травму.</p>
<p><strong>Олег</strong> <em>(тренер)</em>: Девочки, помните, главное - концентрация!</p>
<p><strong>Даша</strong>: Мы помним, Олег!</p>
<p><strong>Царь Онега</strong>: Желаем всем командам удачи! Пусть победит сильнейший! <em>(огонь и дым)</em></p>
<p><em>Когда аплодисменты стихают, Катя, ровняльщица, подходит к центру площадки.</em></p>
<p><strong>Катя</strong>: Чемпионат — это не только игра. Это испытание дружбой, верой и честью. Помните об этом, когда будете стоять на площадке.</p>
<h2 id="сцена-2-тренировка">Сцена 2: Тренировка</h2>
<p><em>Фон - два волейбольных поля. Даша и Аня активно тренируются, делают подачи и защитные приемы, Олег дает указания. На соседнем поле Маруся и Злата проводят свою тренировку.</em></p>
<p><strong>Олег</strong>: Даша, следи за положением рук! Аня, работай ногами!</p>
<p><strong>Даша</strong>: Поняла!</p>
<p><strong>Аня</strong>: Сейчас, Олег!</p>
<p><em>На соседнем поле Злата подает мяч, и в этот момент глаза ее встречаются с глазами Маруси.</em></p>
<p><strong>Злата</strong>: Маруся, у тебя есть план?</p>
<p><strong>Маруся</strong>: Да. На вечеринке после матча мы познакомим Дашу и Аню с Лешей по-отдельности. Они влюбятся и потеряют концентрацию.</p>
<p><strong>Злата</strong>: Отличный план! К тому же, я узнала, что мой отец может подкупить Царя Онегу, чтобы он давил на судью.</p>
<p><strong>Маруся</strong>: Так наша победа в кармане!</p>
<p><strong>Злата</strong>: Но мы не должны расслабляться. Нужно тренироваться и быть готовыми ко всему.</p>
<p><strong>Маруся</strong>: Конечно, но давай не будем забывать и о нашем козыре.</p>
<p><strong>Катя</strong> <em>(выходит на поле для ровняльщицы и слышит их разговор, комментирует)</em>:
На площадке все равны, но только самые стойкие становятся победителями. Иногда победа в игре начинается за ее пределами.</p>
<p><strong>Олег</strong> <em>(случайно услышавший диалог Маруси и Златы)</em>: Девочки, будьте осторожны. Не все играют честно.</p>
<p><img src="/images/blog/smoke-on-onega/party.jpg" alt="Party" /></p>
<h2 id="сцена-3-вечеринка-перед-чемпионатом">Сцена 3: Вечеринка перед чемпионатом</h2>
<p>Место действия: <em>роскошный банкетный зал, нарядные столы, ослепительные огоньки, музыка играет. Гости разговаривают, смеются, танцуют. Даша и Аня находятся по разным сторонам зала.</em></p>
<p><strong>Злата</strong> <em>(обращаясь к Даше)</em>: Даша, дай представить тебе кого-то.</p>
<p><strong>Даша</strong>: Конечно, Злата.</p>
<p><strong>Злата</strong> <em>(подводит Дашу к Леше)</em>: Даша, это Леша, наш будущий судья на матче. Леша, это Даша, одна из лучших волейболисток.</p>
<p><strong>Леша</strong> <em>(улыбаясь)</em>: Очень приятно, Даша. Ты любишь кино?</p>
<p><strong>Даша</strong> <em>(застенчиво)</em>: Да, особенно романтические фильмы. А ты?</p>
<p><strong>Леша</strong>: Я предпочитаю боевики и драмы. Кстати, я видел последний матч твоей команды. Ты замечательно выполнила прием “верхний винт”. Это было впечатляюще.</p>
<p><strong>Даша</strong> <em>(улыбаясь)</em>: Спасибо, Леша. Это одна из моих любимых техник.</p>
<p><em>Между ними заметно напряжение, искры прыгают в глазах.</em></p>
<p><em>Позже, Аня находится у бара, когда Маруся подходит к ней.</em></p>
<p><strong>Маруся</strong>: Аня, есть кто-то, с кем я бы хотела тебя познакомить.</p>
<p><strong>Аня</strong>: С удовольствием, Маруся.</p>
<p><strong>Маруся</strong> <em>(подводит Аню к Леше)</em>: Аня, встречай Лешу. Леша, это Аня.</p>
<p><strong>Леша</strong>: Привет, Аня. Ты любишь кино?</p>
<p><strong>Аня</strong>: Да, я обожаю исторические ленты. Особенно о временах рыцарей. А ты?</p>
<p><strong>Леша</strong>: Мне больше по душе научная фантастика. Кстати, на последнем матче ты использовала прием “забивка”. Это было потрясающе!</p>
<p><strong>Аня</strong>: Спасибо! Это действительно один из моих фаворитов.</p>
<p><em>Между Аней и Лешей также заметно электричество, их взгляды встречаются и задерживаются.</em></p>
<p><em>(Камера поворачивается к другому углу зала)</em></p>
<p><strong>Йоэль</strong> <em>(подходит к Царю Онеге)</em>: Онега, дорогой, как ты думаешь, у нас всё будет под контролем?</p>
<p><strong>Царь Онега</strong>: Я уверен, что все пройдет как надо. Ты же знаешь, мне нужны результаты.</p>
<p><strong>Йоэль</strong>: Прекрасно. Я надеюсь на твою поддержку.</p>
<p><img src="/images/blog/smoke-on-onega/walk.jpg" alt="First Date" /></p>
<h2 id="сцена-4-первое-свидание">Сцена 4: Первое свидание</h2>
<p>Место действия: <em>тенистый парк, скамейки вокруг, легкий шум листьев, играющих на ветру. Даша и Леша идут рядом, на их лицах счастливые улыбки.</em></p>
<p><strong>Леша</strong>: Знаешь, Даша, когда я был младше, я мечтал стать профессиональным волейболистом. Но травма не позволила этого сделать.</p>
<p><strong>Даша</strong>: Это грустно. Но ты стал судьей, и это тоже здорово!</p>
<p><strong>Леша</strong>: Да, я нашел свой путь. А какие у тебя мечты?</p>
<p><strong>Даша</strong>: Кроме волейбола, я всегда хотела путешествовать. Узнавать мир, видеть разные культуры…</p>
<p><strong>Леша</strong> <em>(улыбаясь)</em>: Звучит волшебно. Я бы хотел отправиться в путешествие с тобой.</p>
<p><em>Даша краснеет и смотрит вниз.</em></p>
<p><em>В то время, Аня, скрытая за деревьями, наблюдает за ними. Она сжимает край своей юбки, глаза её наполнены слезами.</em></p>
<p><strong>Аня</strong> <em>(шепотом)</em>: Почему именно она?</p>
<p><em>Катя, проходя мимо, останавливается и глядит на Дашу и Лешу, а затем на Аню.</em></p>
<p><strong>Катя</strong>: Сердце не выбирает, но ответственность может изменить его решение.</p>
<p><em>Аня смотрит на Катю, пытаясь понять, что она имела в виду. Но Катя уходит, оставив Аню в раздумьях.</em></p>
<h2 id="сцена-5-внезапная-травма">Сцена 5: Внезапная травма</h2>
<p>Место действия: <em>волейбольное поле. Даша и Аня играют вместе, их движения скоординированы, но Аня выглядит более агрессивной.</em></p>
<p><strong>Аня</strong> <em>(раздраженно)</em>: Передача, Даша!</p>
<p><strong>Даша</strong>: Спокойнее, Аня, это только тренировка!</p>
<p><em>Аня делает резкое движение за мячом, и ее нога скользит на мокром полу. Она падает, крича от боли.</em></p>
<p><strong>Даша</strong> <em>(бросаясь к ней)</em>: Аня! Ты в порядке?</p>
<p><strong>Аня</strong> <em>(прижимая руку к ноге)</em>: Больно… Я что-то сломала…</p>
<p><strong>Олег</strong> <em>(бегущий к девушкам)</em>: Что произошло?</p>
<p><strong>Даша</strong>: Я не знаю, она просто упала!</p>
<p><em>Катя подходит, пристально смотрит на происходящее.</em></p>
<p><strong>Катя</strong>: Нам нужен врач!</p>
<p><em>Светлана, которая находилась поблизости, бежит к Ане.</em></p>
<p><strong>Светлана</strong>: Дайте мне посмотреть. <em>(Осторожно осматривает ногу Ани)</em> Похоже на вывих лодыжки. Мы должны немедленно ее обработать.</p>
<p><strong>Олег</strong>: А сможет ли она играть в главном матче?</p>
<p><strong>Светлана</strong> <em>(серьезно)</em>: У нее также признаки нервного истощения. Я бы рекомендовала ей отдыхать перед матчем.</p>
<p><strong>Даша</strong>: Но без нее у нас нет шансов победить…</p>
<p><strong>Катя</strong> <em>(философски)</em>: Иногда наше слабое место становится нашим сильнейшим оружием.</p>
<p><strong>Даша</strong> <em>(оглядывая Аню)</em>: Мы обязательно найдем способ победить.</p>
<h2 id="сцена-6-решение">Сцена 6: Решение</h2>
<p>Место действия: <em>раздевалка команды</em>.</p>
<p><strong>Олег</strong> <em>(серьезно)</em>: Ребята, ситуация критическая. Нам придется принять решение. Аня не сможет играть в главном матче из-за травмы.</p>
<p><strong>Даша</strong> <em>(волнуясь)</em>: Что будем делать, Олег? Нам нужна команда, чтобы выйти на площадку!</p>
<p><strong>Олег</strong>: Этот чемпионат важен для нас. Из-за особенностей правил, Даша, ты будешь играть одна. Я договорился с Царем Онегой, и он с радостью разрешил нам сделать это исключение из правил, и выставить на матч одну игрокиню.</p>
<p><strong>Даша</strong> <em>(в шоке)</em>: Одна? Но как? Я не готова!</p>
<p><strong>Олег</strong>: У нас нет другого выбора. Ты сильная игрок, и я верю в тебя.</p>
<p><em>В раздевалке царит напряженная атмосфера. Все игроки волнуются и переживают за Дашу.</em></p>
<p><strong>Маруся</strong> <em>(насмешливо, проходя мимо)</em>: Один на площадке? Удачи тебе, Даша.</p>
<p><strong>Даша</strong> <em>(сжимая кулаки)</em>: Я буду бороться до конца!</p>
<p><em>Катя, занимающаяся своими делами по ровнянию площадки, слышит этот разговор.</em></p>
<p><strong>Катя</strong> <em>(тихо и пророчески)</em>: Один в поле не воин… Но иногда один воин может покорить целое поле.</p>
<h2 id="сцена-7-начало-матча">Сцена 7: Начало матча</h2>
<p>Место действия: <em>Волейбольное поле.</em></p>
<p><em>Царь Онега объявляет начало матча. Болельщики в предвкушении кричат и подбадривают свои команды. Все игроки присутствуют на площадке, готовы к битве.</em></p>
<p><strong>Царь Онега</strong>: Дамы и господа, начинается главный матч этого чемпионата!
<strong>Леша</strong> <em>(подходит к сетке)</em>: Игроки, подготовьтесь!</p>
<p><em>В глазах Леши читается волнение. Ему тяжело судить матч, ведь он знает о том, что происходило за кулисами, и о своих чувствах к Даше.</em></p>
<p><strong>Леша</strong> <em>(в зал)</em>: Предать иль не предать - вот в чем вопрос. Судейства этику нарушить, иль надо оказать сопротивленье? И знать, что этим обрываешь цепь сердечных чувств…</p>
<p><strong>Леша</strong> <em>(ко всем на площадке)</em>: Пусть лучший победит!</p>
<p><em>Матч начинается. Злата и Маруся, используя свою численное преимущество, непрерывно атакуют. Даша отчаянно пытается защититься и атаковать, но она явно устает.</em></p>
<p><strong>Злата</strong> <em>(подсмеиваясь)</em>: Такое ощущение, что ты здесь одна, Даша.</p>
<p><strong>Маруся</strong>: В следующий раз возьми с собой команду!</p>
<p><em>Даша вздыхает, стараясь сосредоточиться на игре, но ей все труднее и труднее.</em></p>
<p><strong>Олег</strong> <em>(кричит с боковой линии)</em>: Даша! Ты справишься!</p>
<p><em>Вдруг Даша почувствовала, что усталость берет верх, и ей становится все сложнее следить за мячом.</em></p>
<p><strong>Светлана</strong> <em>(кричит Олегу)</em>: Возьми тайм-аут!</p>
<p><em>Олег подает сигнал на тайм-аут. Леша останавливает игру.</em></p>
<p><strong>Олег</strong> <em>(быстро подходит к Даше)</em>: Ты в порядке?
<strong>Даша</strong>: Я так устала, Олег. Не знаю, смогу ли я продолжать.</p>
<p><strong>Светлана</strong> <em>(подходит)</em>: Даша, могу предложить тебе релаксационный массаж. Это может помочь.</p>
<p><strong>Даша</strong>: Пожалуйста.</p>
<p><em>Светлана проводит массаж, помогая Даше расслабиться и вернуться в игру.</em></p>
<p><strong>Катя</strong> <em>(проходя мимо)</em>: Сила не в тебе, Даша. Просто найди ее. И пусть <a href="https://en.wikipedia.org/wiki/The_Force#%22May_the_Force_be_with_you%22">пребудет с тобой сила</a>.</p>
<p><img src="/images/blog/smoke-on-onega/trauma.jpg" alt="Trauma" /></p>
<h2 id="сцена-8-финальный-свисток">Сцена 8: Финальный свисток</h2>
<p>Место действия: <em>Волейбольное поле.</em></p>
<p><em>Игра продолжается, и Даша всё больше теряет надежду. Отчаяние отражается в каждом её движении. Злата и Маруся, чувствуя преимущество, продолжают непрерывные атаки.</em></p>
<p><em>Внезапно, из-за кулис появляется Аня. Несмотря на видимую боль в её ноге, она решительно идет на площадку на костылях.</em></p>
<p><strong>Даша</strong> <em>(удивленно)</em>: Аня?! Что ты…</p>
<p><strong>Аня</strong> <em>(перебивая её)</em>: Не важно. Я здесь, чтобы играть. Мы ведь команда.</p>
<p><strong>Олег</strong> <em>(улыбаясь)</em>: Вперёд, девочки!</p>
<p><em>Игра возобновляется, и, благодаря комбинированной стратегии Даши и Ани, девушки начинают отбивать мячи, посылая их обратно к соперникам. Болельщики в восторге, аплодисменты гремят.</em></p>
<p><strong>Злата</strong> <em>(шепчет Марусе)</em>: Нам нужно что-то придумать!</p>
<p><strong>Маруся</strong>: Они слишком сильны вдвоем.</p>
<p><em>Матч продолжается в напряженной атмосфере. Леша, наблюдая за игрой, почти забывает о своей роли судьи, так его завораживает борьба на площадке.</em></p>
<p><em>И вот, после удара Ани, мяч приземляется в зону соперниц. Зал взрывается аплодисментами.</em></p>
<p><strong>Леша</strong> <em>(свистит)</em>: Матч окончен!</p>
<p><strong>Даша</strong> <em>(обнимает Аню)</em>: <a href="https://ru.wikipedia.org/wiki/%D0%A1%D0%BB%D0%B0%D0%B2%D0%B0_%D0%91%D0%BE%D0%B3%D1%83,_%D1%82%D1%8B_%D0%BF%D1%80%D0%B8%D1%88%D1%91%D0%BB!">Слава Богу, ты пришла</a>!</p>
<p><strong>Аня</strong> <em>(с улыбкой)</em>: Я же не могла оставить тебя одну.</p>
<p><strong>Катя</strong> <em>(проходя мимо)</em>: Чемпионы не только из-за таланта, но и из-за дружбы.</p>
<p><em>Все собираются на площадке, обнимаются и поздравляют друг друга.</em></p>
<h2 id="сцена-9-празднование-победы">Сцена 9: Празднование победы</h2>
<p>Место действия: <em>Волейбольное поле. Зрители аплодируют. В центре площадки стоят Даша и Аня, а вокруг них другие участники события.</em></p>
<p><strong>Царь Онега</strong>: Поздравляю, Даша и Аня! Ваша игра была потрясающей. Вы настоящие чемпионки!</p>
<p><strong>Даша</strong>: Спасибо, Ваше Величество!</p>
<p><strong>Аня</strong>: Эта победа — результат нашей командной работы.</p>
<p><strong>Маруся</strong> <em>(с грустью в голосе)</em>: Мы дали всё от себя. <a href="https://translate.vc/en/en-ru/we_did_our_best">Мы сделали своё лучшее</a>.</p>
<p><strong>Злата</strong> <em>(смотря на Йоэля)</em>: Папа, не переживайте. Мы попробуем в следующем году.</p>
<p><strong>Йоэль</strong> <em>(с трибун, явно разъяренно)</em>: Это нечестно! Я отомщу за этот матч!</p>
<p><strong>Олег</strong>: Победа — это не только важный момент. Это также процесс обучения и становления.</p>
<p><strong>Светлана</strong> <em>(подходя вперёд)</em>: Я хотела бы добавить несколько слов. Всегда помните о том, что ваше физическое состояние важно не только на площадке, но и в повседневной жизни. Массаж — это не просто приятная процедура. Это ключевой элемент восстановления организма и поддержания здоровья. Мы видели, как массаж помог Даше собраться с силами во время матча. Так что, не забывайте заботиться о себе! А также спасибо нашему спонсору – компании <a href="https://www.komus.ru/">Комус</a>!</p>
<p><strong>Катя</strong> <em>(подмигивая)</em>: И помните, путь к победе не всегда прямой. Но с верными друзьями и уверенностью в себе, вы можете преодолеть любые препятствия!</p>
<p><img src="/images/blog/smoke-on-onega/kissing.jpg" alt="" /></p>
<h2 id="сцена-10-любовь-побеждает">Сцена 10: Любовь побеждает</h2>
<p>Место действия: <em>Раздевалка, затем на улице. В раздевалке в центре комнаты стоит скамейка, а вокруг неё расположены шкафчики.</em></p>
<p><strong>Даша</strong> <em>(сидит, размышляя)</em>: Что за день! Никогда бы не подумала, что все может так закончиться.</p>
<p><strong>Леша</strong> <em>(входит неуверенно)</em>: Даша, мне нужно с тобой поговорить.</p>
<p><strong>Даша</strong> <em>(удивленно)</em>: Леша? Что ты здесь делаешь? Это же женская раздевалка!</p>
<p><strong>Леша</strong> <em>(с нерешительностью в голосе)</em>: Я… Я хочу признаться тебе в своих чувствах. С того момента, как я встретил тебя, мой мир перевернулся. Я влюблен в тебя. <em>(кадры искр, зажигающие дым над Онегой)</em></p>
<p><strong>Даша</strong> <em>(глаза расширяются от удивления)</em>: Леша, я… я не знала.</p>
<p><strong>Аня</strong> <em>(входит, слыша последние слова)</em>: Даша, я слышала все. И я хочу, чтобы ты знала, что для меня наша дружба и спорт всегда были на первом месте. Я хочу, чтобы ты была счастлива.</p>
<p><strong>Даша</strong> <em>(со слезами на глазах)</em>: Аня, ты моя лучшая подруга. Но…</p>
<p><strong>Аня</strong> <em>(прерывает её, улыбаясь)</em>: Но сердце не может выбирать. Я понимаю.</p>
<p><em>Все радостно обнимаются и выходят на улицу в светлый солнечный день.</em></p>
<p><strong>Катя</strong> <em>(присоединяется ко всем)</em>: В жизни есть много путей, и порой они ведут в разные стороны. Но помните, что любовь и честь — это две стороны одной медали.</p>
<p><em>Все улыбаются, обнимаются. Завеса.</em></p>
<h2 id="заключение">Заключение</h2>
<p><strong>ИИ</strong>:</p>
<p><i>
Мораль сей пьесы такова –<br />
Играйте в волейбол! Ура!<br />
Счастливыми скорее будьте!<br />
Вот мой совет. Не обессудьте.<br />
</i></p>
2023-08-23T00:00:00+00:00https://soshnikov.com/ai/how-to-use-chatgpt-ru/Как использовать ChatGPT и другие большие языковые модели2023-05-20T00:00:00+00:00Dmitri Soshnikovhttp://soshnikov.com/Все вокруг много говорят про ChatGPT и про то, как он оставит людей без работы. А вы уже попробовали беседовать с большими языковыми моделями? Если нет - читайте инструкцию!<p><em>Обновлено 7 ноября 2023</em></p>
<h2 id="tldr">TL;DR</h2>
<p>Это достаточно длинная статья, но если вы пришли сюда чтобы узнать, какие инструменты лучше использовать для работы с большими языковыми моделями, то вот краткий список, в порядке моего личного предпочтения:</p>
<ul>
<li>Официальный сайт <a href="http://chat.openai.com">ChatGPT</a> - через <a href="https://veepn.com/ru/vpn-apps/vpn-for-chrome/">VPN</a> с регистрацией по <a href="https://sms-activate.org/ru">зарубежному SMS</a>. Лучше всего купить подписку на GPT Plus - тогда вам будет доступна последняя версия модели ChatGPT-4, которая заметно лучше ChatGPT-3.5, которая доступна бесплатно.</li>
</ul>
<blockquote>
<p>Не забывайте про VPN, а то ваш аккаунт могут заблокировать!</p>
</blockquote>
<ul>
<li>Российские сервисы YandexGPT (доступен в Алисе в режиме “Давай придумаем”) и <a href="https://developers.sber.ru/gigachat/login">GigaChat</a></li>
<li>Открытая языковая модель <a href="http://openchat.team">OpenChat</a> - в последнее время она становится моим фаворитом, т.к. работает без VPN и регистрации, при этом даёт очень неплохое качество ответов.</li>
</ul>
<div class="text-center"><a class="button alert radius" href="http://openchat.team" target="_blank">Запустить OpenChat</a></div>
<ul>
<li><a href="https://bard.google.com/">Google Bard</a> - модель от Google, требуется VPN из определённых стран</li>
</ul>
<p>Для более подробного обзора доступных вариантов - добро пожаловать в статью!</p>
<h2 id="коротко-о-больших-разговорных-языковых-моделях">Коротко о больших разговорных языковых моделях</h2>
<p>Большие языковые модели - это нейросетевые (как правило, трансформерные) модели, обученные на гигантских объемах текстов для задачи продолжения текстового запроса (промпта). Наиболее известная архитектура таких моделей - GPT, Generative Pre-trained Transformer. OpenAI в настоящий момент обучила модель GPT-4, но есть и ряд других моделей - ruGPT от Сбера, LLaMA от Facebook, GPT-J и др. Веса некоторых моделей (ruGPT, GPT-J) доступны для использования под теми или иными лицензиями, а некоторые модели (в частности, от OpenAI) закрыты, и доступны только в виде API.</p>
<p>Модели предсказания текста дополняют текстовый запрос так, чтобы получился цельный очень правдоподобный текст, похожий на то, что написал бы человек. У них нет цели отвечать на вопросы, поэтому запрос <em>Что такое компьютер?</em> вполне может быть продолжен другим вопросом: <em>Зачем он нужен? Как он устроен?</em>.</p>
<p>Чтобы модель стала вопрос-ответной, её надо доучить на вопрос-ответных парах. В этом случае большинство знаний о мире модель берёт из обычных текстов, а не вопрос-ответных парах лишь учится стилю ответа на вопросы. Вопрос-ответные модели - это нашумевший ChatGPT (на основе GPT-3.5/4), различные обученные сообществом модели на основе LLaMA (Alpaca и др.), или Dolly (от Databricks). Недавно появились также русскоязычные модели - <a href="https://developers.sber.ru/portal/products/gigachat">Gigachat</a> от Сбер, и <a href="https://habr.com/ru/news/736646/">Yandex GPT</a> от Yandex.</p>
<blockquote>
<p>Чтобы хорошо понять (на бытовом и философском уровне), как работают генеративные сети - рекомендую прекрасную статью Стефана Вольфрама <a href="https://writings.stephenwolfram.com/2023/02/what-is-chatgpt-doing-and-why-does-it-work/">What Is ChatGPT Doing … and Why Does It Work?</a></p>
</blockquote>
<h2 id="ограничения-языковых-моделей">Ограничения языковых моделей</h2>
<p>Во всех языковых моделях важное значение имеет <strong>размер контекста</strong>, т.е. максимальная длина промпта. Обычно размер контекста - 1024-4096 токенов, хотя в самой продвинутой версии GPT-4 он составляет 32К. Один токен - это как правило слово или часть слова, в среднем для GPT-3/4 для получения токенов нужно умножить количество слов на 1.5.</p>
<p>Обратите внимание, что диалоговые системы в процессе беседы должны отправлять весь контекст предыдущей беседы на вход языковой модели. Т.е. размер промпта - это не только размер первоначального запроса к сети, но и всех последующих вопросов и ответов в рамках одного диалога. Некоторые диалоговые системы могут укорачивать диалог для передачи в контекст только значимой информации, или симулировать постепенное забывание - но надо понимать, что размер памяти модели в рамках диалога сильно ограничен.</p>
<h2 id="что-насчет-русского">Что насчет русского?</h2>
<p>Большинство упомянутых мною моделей (кроме ruGPT) обучены на большом корпусе английского языка, но также на значительно меньшем объеме других языков, включая русский. Из-за этого они могут (и иногда неплохо) общаться с пользователями на русском языке, но всё равно качество беседы на сравнится с английским. Кроме того, токенизатор GPT разбивает русские тексты на отдельные символы, из-за чего сильно сокращается размер контекста.</p>
<p>Хотя ChatGPT понимает русский “из коробки”, существует альтернативный вариант - перевести текст на английский с помощью нейросетевого переводчика, использовать ChatGPT на английском запросе, и затем перевести ответ назад на русский. Такой подход работает, однако при переводе может потеряться дополнительный смысловой контекст. На практике в каждой конкретной задаче стоит смотреть, какой подход будет работать лучше.</p>
<h2 id="работаем-с-chatgpt">Работаем с ChatGPT</h2>
<p>Наиболее известной вопрос-ответной моделью является ChatGPT, поэтому поговорим, как получить к ней доступ.</p>
<h4 id="сайт-openai">Сайт OpenAI</h4>
<p>Наболее “правильный” способ - это зарегистрироваться на сайте <a href="https://chat.openai.com/">OpenAI</a>. При этом есть тонкости при работе из России - придётся использовать VPN, а также использовать временный номер телефона для регистрации через SMS. В интернете есть пошаговые инструкции по регистрации, например <a href="https://vc.ru/chatgpt/584440-kak-zaregistrirovatsya-v-openai-chatgpt-iz-rossii-v-2023-godu">вот</a>.</p>
<p><img src="/images/blog/chatgpt-webui.png" alt="ChatGPT Web UI" /></p>
<p>Следует отметить, что для использования GPT-4 потребуется платный аккаунт, который можно оплатить только карточками международных систем. Но GPT-3.5 работает без этого, и работает неплохо.</p>
<h4 id="телеграм-боты">Телеграм-боты</h4>
<p>К настоящему времени создано уже множество телеграм-ботов, которые на тех или иных условиях предоставляют доступ к ChatGPT без SMS и регистрации. Основная проблема - найти среди них те, которые под капотом действительно используют оригинальный ChatGPT, и дают достаточное обильный лимит запросов. Можно посмотреть, например:</p>
<ul>
<li><a href="https://openai.ru/">Бота от openai.ru</a></li>
<li><a href="https://t.me/GPT4Telegrambot">GPT4 Telegrambot</a></li>
<li><a href="https://t.me/chatsgpts_bot">ChatGPTs Bot</a></li>
</ul>
<h4 id="bing-search">Bing Search</h4>
<p>Microsoft одним из первых встроил возможности ChatGPT в свой поисковик Bing, а также в браузер Edge. Чтобы получить доступ к соответствующим возможностям - нужно использовать VPN, браузер Microsoft Edge, и залогиниться в свой Microsoft Account.</p>
<p><img src="/images/blog/bingchat-ui.png" alt="Bing Chat UI" /></p>
<p>Особенностью Bing Chat является то, что основной его фокус - поиск информации в интернет! Поэтому он работает немного по-другому: на основании запроса пользователя делает укороченный запрос в интернет, потом обрабатывает полученные результаты и выдает единый ответ, со ссылками на первоисточники. Поэтому пользоваться Bing Chat очень удобно для генерации текстов статей, или для того, чтобы разобраться в какой-то теме.</p>
<h4 id="chrome-extensions">Chrome Extensions</h4>
<p>Если вам не хочется Edge, вы можете поискать <a href="https://chrome.google.com/webstore/search/chat%20gpt">расширения ChatGPT для Chrome</a> - их много разных есть, например <a href="https://merlin.foyer.work/">Merlin ChatGPT</a> [<a href="https://chrome.google.com/webstore/detail/chatgpt-app-for-chrome-me/camppjleccjaphfdbohjdohecfnoikec">установить</a>]</p>
<h4 id="easycode---vs-code-extension">EasyCode - VS Code Extension</h4>
<p>Одним из вариантов использования ChatGPT без ключа может стать расширение <a href="https://www.easycode.ai/">EasyCode</a> для Visual Studio Code. Это расширение, как становится понятным из названия, позволяет удобно работать с кодом - просить ChatGPT объяснить, что делают некоторые функции, или сгенерировать unit-тесты, или написать код. Но с помощью расширения можно делать и запросы к ChatGPT в свободной форме.</p>
<p><img src="/images/blog/vscode-gpt.png" alt="EasyCode in VS Code" /></p>
<p>Достаточно <a href="https://marketplace.visualstudio.com/items?itemName=EasyCodeAI.chatgpt-gpt4-gpt3-vscode">установить расширение</a>, зарегистрироваться с e-mail и паролем (можно использовать <a href="https://temp-mail.org/">temp-mail</a>), ключи OpenAI и VPN не нужны.</p>
<h4 id="приложения-с-chatgpt">Приложения с ChatGPT</h4>
<p>Некоторые продукты уже используют возможности ChatGPT, и интегрируют их в свои приложения. Вот некоторые из них:</p>
<ul>
<li>Поисковик <a href="https://you.com/">you.com</a> позволяет не только искать в интернет на основе ИИ, но и полноценно разговаривать с ChatGPT - это называется <strong>YouChat</strong>. Выбирайте соответствующий пункт и наслаждайтесь! Правда, как видно из примера ниже, на русский запрос иногда следует ответ на английском:</li>
</ul>
<p><img src="/images/blog/youcom-chatgpt-ui.png" alt="You.Com Chat UI" /></p>
<ul>
<li>
<p>Чат-бот <a href="https://chatbot.theb.ai/">Theb.AI</a> визуально очень похож на исходный сайт от OpenAI, при этом работает без регистрации и VPN. В качестве базовой модели используется GPT-3.5.</p>
</li>
<li>
<p><a href="https://www.notion.so">Notion</a> содержит <a href="https://www.notion.so/product/ai">Notion AI</a>. В бесплатной версии можно сгенерировать только 20 продолжений, но <a href="https://vc.ru/u/1309217-ignat-golovin/663223-kak-ispolzovat-notion-ai-besplatno">есть варианты</a>.</p>
</li>
<li>
<p><a href="https://writesonic.com/pricing">ChatSonic</a> - это бот, основанный на ChatGPT, который при этом умеет искать информацию в интернет. Бесплатный режим даёт сгенерировать около 10000 слов в месяц.</p>
</li>
</ul>
<p>Ещё больше аналогов Вы можете найти <a href="https://www.maximonline.ru/guide/5-besplatnykh-analogov-chatgpt-kotorye-dostupny-v-rossii-id882334/">в статье</a></p>
<h4 id="используем-chatgpt-через-api">Используем ChatGPT через API</h4>
<p>Если Вы продвинутый разработчик, то сможете использовать ChatGPT через программный интерфейс - для этого нужно получить ключ для API на сайте OpenAI, пройдя описанный ранее процесс регистрации.</p>
<p>Для изучения того, как использовать ChatGPT для разных задач, очень рекомендую посмотреть курс <a href="https://www.deeplearning.ai/short-courses/chatgpt-prompt-engineering-for-developers/">ChatGPT Prompt Engineering for Developers</a> от <a href="http://deeplearning.ai">deeplearning.ai</a>.</p>
<p><img src="/images/blog/deeplearningai-chat.png" alt="Prompt Engineering for Developers" /></p>
<blockquote>
<p>Кстати, в рамках курса дают готовые jupyter-ноутбуки и возможность поэкспериментировать с ChatGPT API с помощью временного кода. Этим кодом также можно воспользоваться, чтобы получить ненадолго доступ к ChatGPT программно.</p>
</blockquote>
<p>Ещё одна прекрасная возможность - это визуальный конструктор <a href="https://phygital.plus">Phygital.Plus</a>. В этом конструкторе Вы не только сможете использовать ChatGPT саму по себе, но и комбинировать её с другими нейросетями для получения полезного конвейера обработки. Например, можно использовать ChatGPT для улучшения текстового запроса для рисования картинки, и затем сразу закинуть этот запрос в <a href="https://soshnikov.com/scienceart/how-to-use-stable-diffusion-ru/">Stable Diffusion</a>.</p>
<p>Ещё один вариант, близкий к API, но доступный простому пользователю - это использовать <a href="https://huggingface.co/spaces/yizhangliu/chatGPT">HuggingFace Spaces от yizhangliu</a>, где вы можете также поэкспериментировать с “системной” частью запроса ChatGPT, отвечающей за его предварительную настройку до начала диалога. Кроме того, это простой способ попробовать ChatGPT без SMS и регистрации.</p>
<p><img src="/images/blog/chatgpt-hugface.png" alt="HuggingFace" /></p>
<h2 id="другие-генеративные-сети">Другие генеративные сети</h2>
<p>Помимо ChatGPT, существует много других генеративных сетей, которые в том числе распространяются свободно, и которые можно использовать в своих проектах. Обзор таких сетей выходит за рамки данной статьи, но тем не менее я упомяну несколько возможностей, как можно сравнительно легко использовать другие генеративные модели:</p>
<ul>
<li>
<p>Сбер обучает свою диалоговую модель <a href="https://developers.sber.ru/gigachat">GigaChat</a>. Для доступа к ней необходимо залогиниться по SberID. Пока качество бесед с GigaChat оставляет желать лучшего (он слегка уступает ChatGPT на русском языке), но он быстро учится! И это при том, что размер самой модели в 10 раз меньше, чем у GPT-3.5!</p>
</li>
<li>
<p>Недавно вышла модель YandexGPT, которую можно попробовать с помощью Яндекс Алисы, сказав ей <strong>Алиса, давай придумаем</strong>. В настоящее время модель доступна с главной страницы <a href="https://ya.ru">ya.ru</a> и из Яндекс-браузера.</p>
</li>
</ul>
<p><img src="/images/blog/yagptalice-dialog.png" width="30%" /></p>
<ul>
<li>Google выпустил модель <a href="http://bard.google.com">Bard</a>, которая сейчас доступна во многих странах,но не в России. Для использования бота понадобится VPN.</li>
</ul>
<p><img src="/images/blog/bard-chat.png" alt="Bard" /></p>
<ul>
<li>
<p>Модель <a href="https://claude.ai/">Claude 2</a> от Anthropic - говорят, она прямо очень хороша! Из плюсов - она поддерживает загрузку документов, и может отвечать на вопросы по ним, из минусов - требует VPN, и не очень поддерживает русский язык (немного понимает, и даже говорит пару фраз, но что-то сложное отвечать отказывается)</p>
</li>
<li>
<p>Недавно вышла прекрасная открытая модель <a href="https://huggingface.co/openchat/openchat_3.5">OpenChat</a>, которая при небольшом размере в 7 миллиардов параметров может неплохо соперничать с ChatGPT, по крайней мере на тестовых датасетах. По моему опыту, она очень неплохо разговаривает на русском языке. Использовать модель удобно с сайте <a href="http://openchat.team">OpenChat.Team</a>, а ещё можно попробовать её на <a href="https://huggingface.co/spaces/openchat/openchat_3.5">HuggingFace</a></p>
</li>
</ul>
<div class="text-center"><a class="button alert radius" href="http://openchat.team" target="_blank">Запустить OpenChat</a></div>
<ul>
<li>Модель <a href="https://stability.ai/blog/stability-ai-launches-the-first-of-its-stablelm-suite-of-language-models">StableLM</a> от компании <a href="https://stability.ai">Stability.AI</a>, создателей Stable Diffusion, также недавно обзавелась веб-интерфейсом <a href="https://chat.stability.ai">chat.stability.ai</a>. Что примечательно - эта модель доступна без VPN, однако потребуется аутентифицироваться через Google Account или GitHub.</li>
</ul>
<div class="text-center"><a class="button alert radius" href="https://chat.stability.ai" target="_blank">Запустить Stability Chat</a></div>
<ul>
<li>Бот <a href="https://poe.com/">poe.com</a> от создателей сервиса <a href="http://quora.com">Quora</a> позволяет вам из одного веб-интерфейса разговаривать сразу с несколькими языковыми моделями. Используйте режим <strong>Sage</strong> (это по сути тот же ChatGPT-3.5), или поговорите с Claude или DragonFly.</li>
</ul>
<blockquote>
<p>Также в последнее время становится модным использовать локально установленные модели на базе LLaMA, которые могут поместиться в любительские видеокарты с 8-16Gb VRAM, или даже работать на обычном CPU (правда, не очень быстро), или даже <a href="https://arstechnica.com/information-technology/2023/03/you-can-now-run-a-gpt-3-level-ai-model-on-your-laptop-phone-and-raspberry-pi/">на Raspberry Pi</a>! Но это уже тема для отдельной статьи!</p>
</blockquote>
<h2 id="другие-похожие-статьи">Другие похожие статьи</h2>
<p>Если у Вас что-то не получилось сделать по этой инструкции - не отчаивайтесь! Есть ещё много статей про то, как пользоваться ChatGPT - вот некоторые из них:</p>
<ul>
<li><a href="https://journal.tinkoff.ru/chatgpt-in-russia/">Тинькофф-журнал</a></li>
<li><a href="https://tproger.ru/articles/kak-zaregistrirovatsya-v-chatgpt-iz-rossii-v-2023-godu/">На TJournal</a></li>
<li><a href="https://practicum.yandex.ru/blog/kak-polzovatsya-neyrosetyu-chatgpt">На Яндекс.Практикуме</a></li>
<li>Обновляемый сообществом <a href="https://github.com/LiLittleCat/awesome-free-chatgpt/blob/main/README_en.md">список Chat-GPT ботов</a> на GitHub</li>
</ul>
<h2 id="заключение">Заключение</h2>
<p>Надеюсь, благодаря этой статье Вы найдете способ начать пользоваться ChatGPT или похожими моделями! Что бы Вы не делали - обязательно подумайте, как ИИ может облегчить Вам работу, или сделать её более эффективной. Нас ждёт будущее, в котором люди, усиленные возможностями ИИ, смогут решать задачи во много раз эффективнее тех, кто пока не придумал, как ИИ использовать. Будьте в числе первых!</p>
<p>Ну и обязательно помните, что <a href="https://soshnikov.com/ai/never-trust-a-neural-network-ru/">никогда нельзя доверять нейросети</a>…</p>
2023-05-20T00:00:00+00:00https://soshnikov.com/scienceart/how-to-use-stable-diffusion-ru/Как использовать Stable Diffusion и другие нейросети для генерации изображений2023-03-15T00:00:00+00:00Dmitri Soshnikovhttp://soshnikov.com/Вы наверняка слышали про то, что нейросети прекрасно справляются с генерацией изображений! В этой статье я опишу несколько способов начать использовать генеративную сеть Stable Diffusion.<p><em>Обновлено 1 декабря 2023 г.</em></p>
<blockquote>
<p>Эта заметка готовилась как шпаргалка к интенсиву по нейросетевому искусству в <a href="https://arttech.misis.ru/">магистратуре Art and Tech</a> МИСиС, и с тех пор неоднократно обновлялась и использовалась как справочный материал на различных мастер-классах.</p>
</blockquote>
<h2 id="нейросетевые-генеративные-модели">Нейросетевые генеративные модели</h2>
<p>К этому моменту уже все слышали про то, что нейросети позволяют создавать визуально привлекательные изображения по текстовому запросу или по другому изображению. Я <a href="https://soshnikov.com/scienceart/neural-generative-models-and-future-of-art-ru/">ранее писал</a> про то, какую роль могут сыграть современные генеративные нейросети. Посмотреть примеры нейросетевого искусства вы можете в онлайн-галерее <a href="http://experient.art">Experient.Art</a>.</p>
<p>Данная заметка предназначена тем, кто хочет заняться генерацией изображений самостоятельно и не знает, с чего начать. В ней я рассматриваю целый ряд инструментов для нейросетевой генерации изображений, доступных как начинающему пользователю компьютера, так и человеку с опытом программирования (что будет, безусловно, большим плюсом!).</p>
<p>Прежде всего, существует несколько вариантов нейросетевых генеративных моделей:</p>
<ul>
<li>Модели с открытыми весами и исходным кодом, такие, как <a href="https://stability.ai/blog/stable-diffusion-public-release">Stable Diffusion</a> (с запросами на английском языке) или <a href="https://rudalle.ru/">ruDALL-E/Kandinsky</a> (с запросами на русском языке) от Сбер.</li>
<li>Модели, относительно которых известна архитектура, но весов модели нет в свободном доступе (что не позволяет открыто и неограниченно их использовать). Таким моделям относятся <a href="https://imagen.research.google/">Imagen</a> от Google и <a href="https://openai.com/product/dall-e-2">DALL-E 2</a> / <a href="https://openai.com/product/dall-e-3">DALL-E 3</a> от OpenAI. Некоторые из этих моделей могут использоваться через программный интерфейс.</li>
<li>Закрытые модели, предназначенные для коммерциализации в среде художников и дизайнеров. Это в первую очередь <a href="https://www.midjourney.com/">Midjourney</a>. Модель доступна по подписке.</li>
</ul>
<p>Мы в этой статье будем уделять основное внимание Stable Diffusion, как наиболее открытому и привлекательному инструменту, хотя упомянем и об остальных. Также основной акцент сделан на бесплатных инструментах, поскольку они позволяют вам начать экспериментировать без предварительных вложений, и уже затем выбрать наиболее предпочтительный инструмент.</p>
<h2 id="готовые-интерактивные-инструменты">Готовые интерактивные инструменты</h2>
<p>Самыми простыми в использовании инструментами являются различные онлайн-инструменты для генерации изображений, доступные через интернет любому пользователю компьютера. Вам достаточно ввести текстовый запрос, и спустя некоторое время вы получаете сгенерированное изображение. Основным минусом таких инструментов является необходимость оплачивать подписку, хотя некоторое количество изображений вы сможете сгенерировать бесплатно.</p>
<p>К таким инструментам относятся:</p>
<p><img src="/images/blog/dreamstudio1.png" alt="Dream Studio" /></p>
<ul>
<li><a href="https://dreamstudio.ai/">DreamStudio</a> от создателей Stable Diffusion. Вам изначально доступно некоторое количество кредитов, что достаточно для генерации около 500 изображений, но далее - подписка.</li>
<li>Наверное самый известный из всех инструментов <a href="https://www.midjourney.com/">Midjourney</a>. Зарегистрировашись, вы получаете доступ к Discord-сообществу, где можете запрашивать генерацию изображений через бота. Midjourney наверное на сегодняшний день даём лучшее качество художественных изображений, однако его закрытость не позволяет использовать эту модель в более сложных художественных экспериментах. К сожалению, в настоящее время в Midjourney совсем нет бесплатного лимита генерации изображений.</li>
</ul>
<table><tr><td><img src="/images/blog/midjourney1.png" /></td><td><img src="/images/blog/midjourney2.png" /></td></tr></table>
<ul>
<li><a href="https://leonardo.ai/">Leonardo</a> - это попытка сделать аналог Midjourney, основываясь на бесплатных нейросетях типа Stable Diffusion, но при этом добавив несколько своих до-обученных закрытых моделей, более сложный пайплайн обработки и т.д. В результате получился инструмент, который приближается к Midjourney по стабильности и качеству результата, при этом даёт существенно больше возможностей для экспериментов (а также какое-то количество бесплатных генераций в день).</li>
</ul>
<p><img src="/images/blog/leonardo_ai.jpg" alt="Leonardo" /></p>
<ul>
<li>
<p>Инструмент <a href="https://playgroundai.com/">Playground AI</a> позволяет не только генерировать, но и редактировать изображения!</p>
</li>
<li>На сайте <a href="https://stablediffusionweb.com/">Stable Diffusion Web</a> есть небольшой бесплатный генератор, правда, часто очереди на генерацию бывают очень долгими, так что придётся ждать своего изображения несколько десятков минут. Из интересных инструментов, там же есть ещё <a href="https://stablediffusionweb.com/prompts">библиотека промптов</a>, в которой вы можете находить уже сделанные кем-то ранее запросы по ключевым словам, и сразу смотреть на результат.</li>
<li>Набор нейросетевых инструментов <a href="https://neural.love/">Neural.Love</a>, содержащий в том числе генератор изображений. В основном всё хорошее там за деньги, но что-то попробовать можно. Использует свою модель, которая может имитировать разные стили, правда, не всегда успешно.</li>
<li>Бесплатный генератор <a href="https://www.craiyon.com/">Craiyon</a> на основе модели DALL-E Mini.</li>
</ul>
<p><img src="/images/blog/craiyon.png" alt="Craiyon" /></p>
<ul>
<li>Очень мощный набор инструментов для генерации доступен в рамках сервиса <a href="https://phygital.plus/">Phygital+</a>. Он позволяет вам графически комбинировать нейросетевые модели для достижения необходимого художественного результата.</li>
<li>Русскоязычные нейросети семейства <a href="https://rudalle.ru/">ruDALL-E</a> можно <a href="https://rudalle.ru/kandinsky2">протестировать на сайте</a>, или в приложении [Салют]</li>
</ul>
<h2 id="вызов-генеративных-моделей-из-python">Вызов генеративных моделей из Python</h2>
<p>Свободно-распространяемые генеративные модели вроде Stable Diffusion представляют собой обученные нейросети, с которыми можно работать из языка Python. Основной репозиторий таких моделей находится на портале <a href="https://huggingface.co/">HuggingFace</a> - например, вот <a href="https://huggingface.co/models?pipeline_tag=text-to-image&sort=downloads">список моделей Text-to-Image по популярности</a>. Если перейти на страничку модели, то часто можно увидеть пример её использования на языке Python:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">diffusers</span> <span class="kn">import</span> <span class="n">StableDiffusionPipeline</span>
<span class="kn">import</span> <span class="nn">torch</span>
<span class="n">model_id</span> <span class="o">=</span> <span class="s">"runwayml/stable-diffusion-v1-5"</span>
<span class="n">pipe</span> <span class="o">=</span> <span class="n">StableDiffusionPipeline</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span>
<span class="n">model_id</span><span class="p">,</span> <span class="n">torch_dtype</span><span class="o">=</span><span class="n">torch</span><span class="p">.</span><span class="n">float16</span><span class="p">)</span>
<span class="n">pipe</span> <span class="o">=</span> <span class="n">pipe</span><span class="p">.</span><span class="n">to</span><span class="p">(</span><span class="s">"cuda"</span><span class="p">)</span>
<span class="n">prompt</span> <span class="o">=</span> <span class="s">"a photo of an astronaut riding a horse on mars"</span>
<span class="n">image</span> <span class="o">=</span> <span class="n">pipe</span><span class="p">(</span><span class="n">prompt</span><span class="p">).</span><span class="n">images</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">image</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="s">"astronaut_rides_horse.png"</span><span class="p">)</span>
</code></pre></div></div>
<p>Для работы таких моделей необходимо наличие графического процессора GPU, причем зачастую необходима достаточно мощная модель с 16Gb памяти <abbr title="Video RAM, память видеокарты">VRAM</abbr> и более (хотя простая генерация может заработать на 8Gb или даже 4Gb, т.е. на картах “домашнего” уровня).</p>
<p>Есть несколько способов запуска таких моделей:</p>
<ul>
<li>Используя общедоступные облачные инструменты с поддержкой GPU, например, <a href="http://colab.research.google.com">Google Colab</a>. Он позволяет вам использовать GPU по нескольку часов в день даже на бесплатном тарифе, а небольшая ежемесячная оплата снимает это ограничение. В интернет есть очень много заготовленных примеров, использующих именно бесплатный уровень Colab для работы.</li>
<li>Используя облачные сервера, например, Sber Cloud, Yandex Cloud или Microsoft Azure. Плюсом является то, что вы платите только за время использования GPU, что позволяет запускать ресурсоёмкие процессы без капиталовложений.</li>
<li>На домашнем компьютере - в этом случае вам нужно будет обзавестись графическим ускорителем и <a href="/education/how-to-execute-notebooks-from-github-ru/">установить себе среду Python</a> с поддержкой GPU.</li>
</ul>
<h2 id="automatic-1111">AUTOMATIC 1111</h2>
<p>Стандартом <em>де факто</em> для установки Stable Diffusion к себе на компьютер является пакет <a href="https://github.com/AUTOMATIC1111/stable-diffusion-webui">Stable Diffusion Web-UI</a>, известный также как AUTOMATIC 1111 (по псевдониму своего создателя). Это расширяемое окружение, запускающееся на вашем компьютере, и открывающее доступ ко множеству различных моделей для генерации через интерактивный веб-интерфейс (я чуть было не написал <em>удобный</em>, но нет - интерфейс может показаться немного пугающим для типовых пользователей).</p>
<p>Установить AUTOMATIC можно как на Windows с видеокартой NVIDIA, так и на Mac с процессорами M1/M2. Сам процесс установки максимально упрощен и автоматизирован, и вы найдёте его описание <a href="https://github.com/AUTOMATIC1111/stable-diffusion-webui">в репозитории проекта</a>.</p>
<p><img src="/images/blog/stable-diffusion-webui.png" alt="AUTOMATIC 1111" /></p>
<p>Очень удобной возможностью является запуск AUTOMATIC 1111 из Google Colab - в этом случае вы используете бесплатные вычислительные мощности Google, при этом получаете в своё распоряжение веб-интерфейс для генерации со всеми новыми возможностями, поддерживаемыми Stable Diffusion WebUI. Вот несколько вариантов готовых Colab-ноутбуков для запуска AUTOMATIC:</p>
<ul>
<li><a href="https://github.com/camenduru/stable-diffusion-webui-colab">Рекомендованный вариант</a> от Camenduru</li>
<li><a href="https://github.com/TheLastBen/fast-stable-diffusion">Fast Stable Diffusion</a> от TheLastBen</li>
</ul>
<p>Процесс запуска во всех случаях примерно такой:</p>
<ul>
<li>Заходите в репозиторий проекта</li>
<li>Находите ссылку на последнюю версию ноутбука Google Colab</li>
<li>Запускаете её</li>
<li>В Colab прокликиваете все ячейки по очереди, устанавливая где необходимо какие-то параметры. В конце после запуска WebUI вы должны получить ссылку на AUTOMATIC WebUI, которую можно будет открыть в соседней вкладке, и наслаждаться генерацией.</li>
</ul>
<div class="text-center"><a class="button alert radius" href="https://github.com/camenduru/stable-diffusion-webui-colab" target="_blank">Запустить AUTOMATIC WebUI</a></div>
<h2 id="работа-в-python--google-colab">Работа в Python / Google Colab</h2>
<p>Наибольшую гибкость предоставляет использование нейросетевых моделей непосредственно из среды Python. Например, так вы сможете автоматически генерировать множество изображений по набору запросов, или же перебирать параметры генерации и создавать много изображений “на выбор”. Более того, самые продвинутые нейросетевые техники, вроде генерации стилизованного видео, становятся доступны именно программистам, знакомым с “внутренним устройством” моделей.</p>
<p>Однако, даже имея небольшие навыки программирования или немного здравого смысла, вы уже сможете воспользоваться готовыми примерами в Google Colab. Вот несколько полезных примеров:</p>
<ul>
<li><a href="http://eazify.net/ru/sdworkbook">Stable Diffusion Workbook</a>, который я для вас с любовью подготовил - генерация с помощью нескольких моделей Stable Diffusion, включая режим Image-to-Image и Upscaling</li>
</ul>
<div class="text-center"><a class="button alert radius" href="http://eazify.net/ru/sdworkbook" target="_blank">Запустить Stable Diffusion Workbook</a></div>
<ul>
<li>Русскоязычные модели <a href="https://github.com/ai-forever/Kandinsky-2">Kandinsky 2.2</a> и предыдущее поколение <a href="https://github.com/ai-forever/ru-dalle">ruDALL-E</a>. Обратите также внимание на <a href="https://github.com/shonenkov-AI/rudalle-aspect-ratio">ruDALL-E Aspect Ratio</a>, позволяющее получить изображения нестандартных форматов</li>
<li>Очень много примеров использования различных нейросетевых моделей есть в <a href="https://github.com/camenduru">GitHub cameduru</a>. На первой странице приведён постоянно обновляющийся список различных colab-ов.</li>
<li>Предыдущее поколение инструментов генерации на основе VQGAN+CLIP:
<ul>
<li><a href="https://colab.research.google.com/github/justinjohn0306/VQGAN-CLIP/blob/main/VQGAN%2BCLIP(Updated).ipynb#scrollTo=ZdlpRFL8UAlW">От Katherine Crowson</a></li>
<li><a href="https://colab.research.google.com/github/robgon-art/GANshare/blob/main/GANshare_One.ipynb">GANShare One</a></li>
</ul>
</li>
</ul>
<blockquote>
<p>Описанные здесь инструменты и некоторые другие доступны в моём репозитории <a href="http://github.com/shwars/AI_Art_Workbooks">AI Art Workbooks</a>.</p>
</blockquote>
<h2 id="обучение-своих-моделей-dreambooth-textual-inversion-lora">Обучение своих моделей: DreamBooth, Textual Inversion, LoRA</h2>
<p>Наиболее интересная особенность Stable Diffusion состоит в том, что существуют способы до-обучить модель на своих изображениях. Это имеет смысл делать в двух случаях:</p>
<ul>
<li>До-обучить модель для изображения специфических <strong>предметов, объектов или людей</strong></li>
<li>До-обучить модель какому-то оригинальному <strong>стилю</strong></li>
</ul>
<p>В обоих случаях можно обойтись небольшим количеством фотографий: говорят, что можно брать около 5-10 фото, хотя в моём случае хорошие результаты с портретами людей стали получаться с датасетами в районе 100-200 фото.</p>
<blockquote>
<p>Фотографии для обучения стоит заранее привести к требуемому размеру (512x512), при этом выбирая по возможности только хорошие качественные фотографии с правильной композицией.</p>
</blockquote>
<p>Есть несколько алгоритмов до-обучения модели:</p>
<ul>
<li><strong>DreamBooth</strong> до-обучает исходную модель целиком, в этом случае нам приходится хранить новую модель размером около 5Gb и использовать её. <a href="https://colab.research.google.com/github/ShivamShrirao/diffusers/blob/main/examples/dreambooth/DreamBooth_Stable_Diffusion.ipynb">Рекомендуемый DreamBooth Colab</a></li>
<li><strong>Textual Inversion</strong> - это подход, при котором для нового объекта или стиля подбираются правильные семантические векторы, а сам процесс генерации изображения остаётся неизменным. В итоге необходимо сохранять лишь часть текстовой модели, кроме того, появляется возможность комбинировать несколько текстовых инверсий в одном изображении. Качество текстовой инверсии обычно несколько уступает DreamBooth.</li>
<li><strong>LoRA</strong> (Low-Rank Adaptation) - это один из самых современных подходов, при котором сохраняются все плюсы DreamBooth, но при этом обучается не целиком исходная нейросеть (у которой большое количество параметров), а только “дельта”, которую можно с незначительной потерей точности представить как разложение на две матрицы меньшего ранга. В результате процесс обучения происходит намного быстрее, а результирующие веса занимают существенно меньше места на диске.</li>
</ul>
<h2 id="каталоги-предобученных-моделей">Каталоги предобученных моделей</h2>
<p>По мере того, как процесс до-обучения моделей становится всё более простым, многие участники сообщества до-обучают свои модели и делятся ими с сообществом. Большую коллекцию таких обученных моделей для разных случаев жизни можно найти на сайте <a href="https://civitai.com/">CIVITAI</a>. Некоторые из наиболее известных моделей:</p>
<ul>
<li><a href="https://civitai.com/models/1087/inkpunk-diffusion">InkPunk Diffusion</a> - специфический художественный стиль</li>
<li><a href="https://civitai.com/models/4201?modelVersionId=130072">Realistic Vision</a> - для фотореалистичных объектов</li>
<li><a href="https://huggingface.co/Linaqruf/anything-v3.0">Anything v3</a> - аниме</li>
</ul>
<p>Вы можете использовать эти модели как из программного кода, так и из инструментов типа Stable Diffusion Web UI. В последнем случае вам нужно следовать инструкции и положить веса модели в соответствующую папку на диске.</p>
<h2 id="ещё-несколько-полезных-колабов">Ещё несколько полезных колабов</h2>
<p>Примеры ниже не связаны напрямую с генерацией изображений, но могут оказаться интересными!</p>
<ul>
<li><a href="http://eazify.net/ru/stworkbook">Style Transfer</a> - Пример Style Transfer для изображений и для видео</li>
<li><a href="http://eazify.net/ru/tgworkbook">Генерация текста с LSTM</a> - пример обучения нейросети для генерации текста по символам или по словам</li>
</ul>
<blockquote>
<p>Данный список будет постепенно дополняться</p>
</blockquote>
<p>Надеюсь, этот список поможет вам разобраться с тем, как начать использовать нейрогенерацию изображений! Если всё получилось, очень рекомендую устроить <a href="https://soshnikov.com/scienceart/neural-generative-models-and-future-of-art-ru/#neurogenerative-parties">нейрогенеративную вечеринку</a></p>
2023-03-15T00:00:00+00:00