<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Властелин машин</title><generator>teletype.in</generator><description><![CDATA[Всё о передовых инструментах исследования и трюках продуктивности, чтобы стать крутым аналитиком и специалистом по машинному обучению на Python.]]></description><image><url>https://img4.teletype.in/files/7c/f8/7cf86c55-9652-482e-b44a-f31a7c42cf6b.png</url><title>Властелин машин</title><link>https://teletype.in/@dt_analytic</link></image><link>https://teletype.in/@dt_analytic?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/dt_analytic?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/dt_analytic?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Sat, 04 Apr 2026 09:24:24 GMT</pubDate><lastBuildDate>Sat, 04 Apr 2026 09:24:24 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@dt_analytic/lH04tbPQ_6a</guid><link>https://teletype.in/@dt_analytic/lH04tbPQ_6a?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic</link><comments>https://teletype.in/@dt_analytic/lH04tbPQ_6a?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic#comments</comments><dc:creator>dt_analytic</dc:creator><title>Решение задач с разбиением чисел на цифры</title><pubDate>Thu, 12 Feb 2026 04:51:09 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/2d/46/2d46ba66-f872-4644-af25-76187d389b26.png"></media:content><category>математика</category><description><![CDATA[<img src="https://img1.teletype.in/files/06/86/0686df17-fb7d-4c51-b6d8-a24cb937e09b.png"></img>Часто в задачах требуется не просто оперировать числом как целым, а разложить его на отдельные цифры — как разобрать механизм на детали. Рассмотрим простой, но эффективный подход.]]></description><content:encoded><![CDATA[
  <figure id="hzod" class="m_original">
    <img src="https://img1.teletype.in/files/06/86/0686df17-fb7d-4c51-b6d8-a24cb937e09b.png" />
  </figure>
  <p id="0D3t">Часто в задачах требуется не просто оперировать числом как целым, а разложить его на отдельные цифры — как разобрать механизм на детали. Рассмотрим простой, но эффективный подход.</p>
  <p id="9iQk">Для этого вспомним, что мы можем подсчитать целую и остаточную части при делении на 10 (или другое число, смотря в какой системе счисления работаем). </p>
  <p id="T4Et">Допустим, имеем задачу: </p>
  <p id="8gTt">&quot;Дано 32-битное знаковое целое число x. Верните x с перевернутыми цифрами. Если переворачивание x приводит к выходу значения за пределы диапазона 32-битных знаковых целых чисел [-2**31, 2**31 - 1], верните 0&quot;</p>
  <p id="mMCP">Ключевая часть алгоритма к решению может выглядеть так - итеративно, пока целая часть от деления на 10 не равна 0 берем дробную и добавляем к результату, умноженному на 10:</p>
  <pre id="yX7v" data-lang="python">def f(x):

    num = 2**31
    min_x = -num
    max_x = num - 1

    if x&lt;0:
        sign = -1
        x = -x
    else:
        sign = 1

    res = 0

    while x!=0:
        ost = x%10
        x = x//10
        res = res*10 + ost

        if (res&lt;min_x) or (res&gt;max_x):
            return 0

    return res*sign

f(123)</pre>
  <p id="x0Qr"></p>
  <figure id="RqIk" class="m_original">
    <img src="https://img4.teletype.in/files/7b/94/7b94f7cb-6fd8-4282-8d5d-8b7336ae7571.png" width="615" />
  </figure>
  <p id="cWZ0">Чтобы не мучиться с остатком, посчитаем знак и будем работать с положительным x. Напомню, что остаток в Python имеет знак делителя и работает правило:</p>
  <p id="fJBJ"><strong><code>a % b = a - b * floor(a / b)</code></strong>, </p>
  <p id="Hriv">floor(a / b) — округление вниз до ближайшего целого.</p>
  <p id="iTxz">7%3 = 7 - 3*2=1</p>
  <p id="FiTN">7%-3 = 7 + 3(-3) = -2</p>
  <p id="MMpd">-7%-3 = -7 + 3*(2) = -1</p>
  <p id="wT79">-7%3 = -7 - 3*(-3)=2</p>
  <p id="FKAv"></p>
  <p id="Gd2W">Логика аналогичная, если надо положительное целое из десятичной перевести в двоичную систему:</p>
  <pre id="VBmk" data-lang="python">x = 123
s = &#x27;&#x27;
while x!=0:
    ost = x%2
    s = f&#x27;{ost}{s}&#x27;
    x  = x//2</pre>
  <figure id="29F2" class="m_column">
    <img src="https://img3.teletype.in/files/e9/b3/e9b3b67c-d169-4b31-b0ab-2142a54c1c57.png" width="600" />
  </figure>
  <p id="ATXm">и обратно:</p>
  <pre id="jh7p" data-lang="python">res = 0
s_len = len(s)
for i, num in enumerate(s):
    res = res + int(num)*2**(s_len-1-i)
res</pre>
  <figure id="Dv29" class="m_original">
    <img src="https://img2.teletype.in/files/51/07/5107e462-ccf9-41e0-a75d-f23d14ccb75b.png" width="555" />
  </figure>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dt_analytic/qv-WSVJ98O8</guid><link>https://teletype.in/@dt_analytic/qv-WSVJ98O8?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic</link><comments>https://teletype.in/@dt_analytic/qv-WSVJ98O8?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic#comments</comments><dc:creator>dt_analytic</dc:creator><title>BM25Retriever под капотом</title><pubDate>Tue, 10 Feb 2026 01:09:41 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/ee/4e/ee4e4b33-f282-4da5-a265-0add4b82dc91.png"></media:content><category>обработка данных</category><tt:hashtag>bm25retriever</tt:hashtag><tt:hashtag>retrievers</tt:hashtag><tt:hashtag>llm</tt:hashtag><tt:hashtag>rag</tt:hashtag><tt:hashtag>tfidf</tt:hashtag><tt:hashtag>nlp</tt:hashtag><tt:hashtag>ml</tt:hashtag><description><![CDATA[<img src="https://img2.teletype.in/files/10/23/1023b3ba-221a-4612-a8e0-a0a0c4bfd241.png"></img>В современных rag системах центральным инструментом являются ретриверы - объекты, которые отвечают за поиск близкой к запросу информации (контекста). Одним из них является BM25Retriever, основанный на частоте встречаемости. В отличие от аналогов, использующих векторные представления, он полагается на точные совпадение единиц, на которые разбит текст (токенов).]]></description><content:encoded><![CDATA[
  <figure id="vKCE" class="m_column">
    <img src="https://img2.teletype.in/files/10/23/1023b3ba-221a-4612-a8e0-a0a0c4bfd241.png" width="696" />
  </figure>
  <p id="8ppD">В современных rag системах центральным инструментом являются ретриверы - объекты, которые отвечают за поиск близкой к запросу информации (контекста). Одним из них является <strong>BM25Retriever</strong>, основанный на частоте встречаемости. В отличие от аналогов, использующих векторные представления, он полагается на точные совпадение единиц, на которые разбит текст (токенов).</p>
  <p id="4MUd">Для демонстрационных целей возмем набор текстов с описанием компьютерных угроз с сайта MITRE ATT&amp;CK (<a href="https://attack.mitre.org/techniques/T1593/" target="_blank">тут </a>и <a href="https://attack.mitre.org/techniques/T1570/" target="_blank">тут</a>):</p>
  <pre id="Lx5Y" data-lang="python">import numpy as np

train_sents = [&quot;Contagious Interview has utilized open-source indicator of compromise repositories to determine their exposure to include VirusTotal, and MalTrail&quot;,
&quot;Kimsuky has used LLMs to identify think tanks, government organizations, etc. that have information&quot;,
&#x27;&#x27;&#x27;Sandworm Team researched Ukraine&#x27;s unique legal entity identifier (called an &quot;EDRPOU&quot; number), including running queries on the EDRPOU website, in preparation for the NotPetya attack. Sandworm Team has also researched third-party websites to help it craft credible spearphishing emails&#x27;&#x27;&#x27;,
&quot;During the 2015 Ukraine Electric Power Attack, Sandworm Team moved their tools laterally within the corporate network and between the ICS and corporate network&quot;,
&quot;During the 2022 Ukraine Electric Power Attack, Sandworm Team used a Group Policy Object (GPO) to copy CaddyWiper&#x27;s executable msserver.exe from a staging server to a local hard drive before deployment&quot;]


query_sent=&quot;Mustang Panda has used open-source research to identify information about victims to use in targeting to include creating weaponized phishing lures and attachments&quot;</pre>
  <figure id="COJQ" class="m_column">
    <img src="https://img2.teletype.in/files/db/95/db95d9b9-59ee-4b1f-93d0-f0ea00e7c161.png" width="1385" />
  </figure>
  <h2 id="JZpA">BM25Retriever</h2>
  <p id="uztG">Для корректной работы <strong>BM25Retriever </strong>важен способ разбиения текста на единицы, для этого используется параметр <strong>preprocess_func</strong>. Зададим функцию, осуществляющую деление по словам и их стемминг:</p>
  <pre id="afb0" data-lang="python">import nltk
from nltk.stem import SnowballStemmer
nltk.download(&#x27;punkt_tab&#x27;)

def preprocess_func(text):
    text = text.lower()
    words_l = nltk.tokenize.word_tokenize(text)
    stemmer = SnowballStemmer(&#x27;english&#x27;)
    words_l = [stemmer.stem(w) for w in words_l]
    return words_l</pre>
  <figure id="T9iL" class="m_column">
    <img src="https://img1.teletype.in/files/08/a6/08a65028-7b6e-4e29-a676-6476a798fa5b.png" width="1326" />
  </figure>
  <p id="UNDL">Верхнеуровнево для работы надо уметь создать <strong>BM25Retriever</strong>, используя, например, метод <strong>from_texts </strong>и найти ближайшие тексты с <strong>invoke</strong>:</p>
  <pre id="Z3DS" data-lang="python">from langchain_community.retrievers import BM25Retriever

# Возвращается объект pydantic BaseModel (в __init__ Serializable)
bm_retriever = BM25Retriever.from_texts(train_sents, k=2, preprocess_func=preprocess_func,
                                        metadatas=[{&#x27;len&#x27;:len(it)} for it in train_sents],
                                        ids=range(len(train_sents)))

bm_retriever.invoke(query_sent)
</pre>
  <figure id="G3bV" class="m_column">
    <img src="https://img4.teletype.in/files/70/1d/701d43af-df55-458c-876e-f7bf3e5bda31.png" width="1401" />
  </figure>
  <h3 id="wWa0">from_texts</h3>
  <hr />
  <p id="PacQ"><a href="https://github.com/langchain-ai/langchain-community/blob/libs/community/v0.4.1/libs/community/langchain_community/retrievers/bm25.py#L62" target="_blank">from_texts</a> получает аргументы:</p>
  <ul id="z5nI">
    <li id="ALPV"><strong>texts </strong>- список текстов, формирующих базу для поиска;</li>
    <li id="7WBp"><strong>k </strong>- количество ближайших текстов в ответ на запрос поиска;</li>
    <li id="bNFg"><strong>preprocess_func </strong>- функция для разбиения текстов на токены;</li>
    <li id="J17x"><strong>metadatas </strong>- список словарей с метаданными для каждого текста в texts;</li>
    <li id="gqJC"><strong>ids </strong>- список идентификаторов для каждого текста в texts.</li>
  </ul>
  <p id="8HcP">Следует отметить, что k можно поменять (например, когда вы загружаете настроенный дамп retriever-а) так: <strong>bm_retriever.k = 1</strong>.</p>
  <p id="YkM5"><strong>from_texts </strong>выполняет следующий код:</p>
  <ul id="1Np1">
    <li id="MFZa">texts_processed = [preprocess_func(t) for t in texts]</li>
    <li id="Klam">vectorizer = BM25Okapi(texts_processed...)</li>
  </ul>
  <h3 id="X4KQ">invoke</h3>
  <hr />
  <p id="Nowy">В <strong>invoke </strong>выполняются:</p>
  <ul id="7iaj">
    <li id="YsJ2"><a href="https://github.com/langchain-ai/langchain/blob/langchain-core%3D%3D1.2.8/libs/core/langchain_core/retrievers.py#L226" target="_blank">invoke из BaseRetriever</a> вызывает <strong>_get_relevant_documents</strong>;</li>
    <li id="oPcC"><a href="https://github.com/langchain-ai/langchain-community/blob/libs/community/v0.4.1/libs/community/langchain_community/retrievers/bm25.py#L111" target="_blank">_get_relevant_documents</a> осуществляет препроцессинг и возврат наиболее подходящих через <strong>get_top_n</strong>:</li>
    <ul id="pFzf">
      <li id="oyMG">processed_query = self.preprocess_func(query)</li>
      <li id="acbx">self.vectorizer.get_top_n(processed_query, self.docs, n=self.k)</li>
    </ul>
  </ul>
  <p id="cSuT">Подытоживая, <strong>from_texts </strong>инициирует препроцессинг текстов и создает класс <strong>BM25Okapi</strong>, а <strong>invoke </strong>- препроцессинг запроса и вызывает метод <strong>get_top_n </strong>объекта класса BM25Okapi. Теперь разберемся с BM25Okapi и его особенностями.</p>
  <h2 id="eqck">okapi</h2>
  <ul id="awAt">
    <li id="fGJm">в конструкторе подсчитываются:</li>
    <ul id="irFD">
      <li id="g7mx"><a href="https://github.com/dorianbrown/rank_bm25/blob/0.2.2/rank_bm25.py#L42" target="_blank">словарь встречаемости слов в документах (doc_freqs)</a></li>
      <li id="B0Zz"><a href="https://github.com/dorianbrown/rank_bm25/blob/0.2.2/rank_bm25.py#L34" target="_blank">длины каждого документа в токенах (doc_len) и средняя длина документа в токенах (avgdl)</a></li>
      <li id="0FLr"><a href="https://github.com/dorianbrown/rank_bm25/blob/0.2.2/rank_bm25.py#L85" target="_blank">idf токенов (idf)</a></li>
    </ul>
    <li id="vshW">в <a href="https://github.com/dorianbrown/rank_bm25/blob/0.2.2/rank_bm25.py#L69" target="_blank">get_top_n</a>:</li>
    <ul id="zEZo">
      <li id="83aP">каждый документ (объект с текстом и метаданными) получает скор близости к запросу <a href="https://github.com/dorianbrown/rank_bm25/blob/0.2.2/rank_bm25.py#L107" target="_blank">get scores in BM25Okapi</a></li>
      <li id="kAwc">документы сортируются по убыванию скора и выбирается заданное число</li>
      <li id="bvAq">соответствующий код:</li>
    </ul>
    <ul id="CkO1">
      <ul id="vYwT">
        <li id="UgZM">scores = self.get_scores(query)</li>
        <li id="ttV6">top_n = np.argsort(scores)[::-1][:n]</li>
        <li id="PX1x">[documents[i] for i in top_n]</li>
      </ul>
    </ul>
  </ul>
  <p id="HvQu">Создадим вручную объект класса BM25Okapi и набор документов (без метаданных для простоты):</p>
  <pre id="AVp6" data-lang="python">from rank_bm25 import BM25Okapi
from langchain_core.documents import Document


texts_processed = [preprocess_func(t) for t in train_sents]
vectorizer = BM25Okapi(texts_processed)

docs = [Document(page_content=t) for t in train_sents]

processed_query = preprocess_func(query_sent)
</pre>
  <figure id="iHLt" class="m_column">
    <img src="https://img3.teletype.in/files/28/cb/28cb8e7f-8d13-4f7e-a798-9c4b0c4cfa63.png" width="1281" />
  </figure>
  <h3 id="9Hm3">свойства</h3>
  <ul id="LCRx">
    <li id="p6p5"><strong>idf </strong>- содержит idf токенов</li>
    <li id="oZ8S"><strong>doc_freqs </strong>- содержит список словарей с частотами токенов для каждого документа</li>
    <li id="MFXP"><strong>get_scores </strong>- возвращает список скоров каждого документа относительно query</li>
    <li id="c98p"><strong>get_top_n </strong>- возвращает топ k самых близких документов</li>
    <li id="FoiH"><strong>doc_len </strong>- содержит список количества токенов в каждом документе</li>
    <li id="UH3l"><strong>avgdl </strong>- содержит среднее количество токенов в документах</li>
  </ul>
  <hr />
  <h3 id="BlfV">формула скора</h3>
  <hr />
  <p id="Oaj4">Посчитаем вручную скор для нулевого документа, который возвращает <strong>get_scores</strong>:</p>
  <pre id="YOVQ" data-lang="python">vectorizer.get_scores(processed_query)</pre>
  <figure id="vQdK" class="m_column">
    <img src="https://img4.teletype.in/files/76/c0/76c03c51-3288-4b3b-9778-968474419339.png" width="1014" />
  </figure>
  <p id="9Tyq">idf для каждого токена считается в BM25Okapi конструкторе по следующей формуле: </p>
  <figure id="RBCs" class="m_column">
    <img src="https://img4.teletype.in/files/3a/c0/3ac07920-6135-40cd-93af-2c2312556ac0.png" width="1140" />
  </figure>
  <p id="KWcx">Добавление константы (0.5) не дает знаменателю или числителю стать равным нулю и сглаживает рост idf для слов, которые встречаются только в единичных документах.</p>
  <p id="vway">Посчитаем idf для токена &quot;open-sourc&quot;, который встречается в 1 из 5 текстов:</p>
  <pre id="R0Rf" data-lang="python">idf = np.log((5-1+0.5)/(1+0.5))
vectorizer.idf[&#x27;open-sourc&#x27;], idf</pre>
  <figure id="Rxj2" class="m_column">
    <img src="https://img1.teletype.in/files/c8/d4/c8d47502-d452-4a08-adff-3962c56eb6b3.png" width="1082" />
  </figure>
  <p id="0AwX">tf для &quot;open-sourc&quot; в нулевом документе получим так:</p>
  <pre id="uYx8" data-lang="python">N = 0
tf = vectorizer.doc_freqs[N][&#x27;open-sourc&#x27;]
tf</pre>
  <figure id="xtld" class="m_column">
    <img src="https://img4.teletype.in/files/78/95/7895bdb6-1152-4085-a7df-19d3f7c315b1.png" width="1084" />
  </figure>
  <p id="iImJ">Для извлечения скора документа надо итерировать по всем токенам query и посчитать сумму для каждого по формуле:</p>
  <figure id="r8RD" class="m_column">
    <img src="https://img3.teletype.in/files/6e/c5/6ec53279-98c3-4fed-8acb-f85398c8f502.png" width="1115" />
  </figure>
  <p id="1L6a">Из формулы следует, что для документов с длиной меньше средней слагаемое будет увеличено (короткие получают бонус), а для больше средней - уменьшено (штраф за размытость). Посчитаем добавку для токена &quot;open-sourc&quot;:</p>
  <pre id="YiFs" data-lang="python">idf*(tf * (1.5 + 1) /(tf + 1.5 * (1 - 0.75 + 0.75 * vectorizer.doc_len[N] / vectorizer.avgdl)))</pre>
  <figure id="WI8l" class="m_column">
    <img src="https://img1.teletype.in/files/8d/e0/8de0797d-9bc1-4588-b849-f45397cac677.png" width="1179" />
  </figure>
  <p id="dF6m">А теперь выведем скор для всего нулевого документа:</p>
  <pre id="DPKV" data-lang="python">score = 0
# для нулевого документа
for q in processed_query:
    q_freq = vectorizer.doc_freqs[N].get(q,0)
    adding = vectorizer.idf.get(q, 0) * (q_freq * (1.5 + 1) /
                                        (q_freq + 1.5 * (1 - 0.75 + 0.75 * vectorizer.doc_len[N] / vectorizer.avgdl)))
    if q==&#x27;open-sourc&#x27;:
      print(f&#x27;принт добавка от &quot;open-sourc&quot; - {adding}&#x27;)
    score += adding
score</pre>
  <figure id="eOmQ" class="m_column">
    <img src="https://img4.teletype.in/files/b9/62/b962361c-a6ca-463d-a46b-7231f6c8b3a8.png" width="1128" />
  </figure>
  <p id="3mS2">По скору наш (нулевой) документ второй, в такой очередности он и выводится в <strong>get_top_n</strong>:</p>
  <pre id="hgOk" data-lang="python">k = 2

vectorizer.get_top_n(processed_query, docs, n=k)</pre>
  <figure id="uAWc" class="m_column">
    <img src="https://img3.teletype.in/files/63/e1/63e1f429-eabe-4743-8aa7-2e39ad6b8443.png" width="1125" />
  </figure>
  <tt-tags id="dQtX">
    <tt-tag name="bm25retriever">#bm25retriever</tt-tag>
    <tt-tag name="retrievers">#retrievers</tt-tag>
    <tt-tag name="llm">#llm</tt-tag>
    <tt-tag name="rag">#rag</tt-tag>
    <tt-tag name="tfidf">#tfidf</tt-tag>
    <tt-tag name="nlp">#nlp</tt-tag>
    <tt-tag name="ml">#ml</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dt_analytic/JWvPr50fMW1</guid><link>https://teletype.in/@dt_analytic/JWvPr50fMW1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic</link><comments>https://teletype.in/@dt_analytic/JWvPr50fMW1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic#comments</comments><dc:creator>dt_analytic</dc:creator><title>Многоканальное логирование для python проекта</title><pubDate>Fri, 04 Jul 2025 05:02:30 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/8c/9f/8c9fc929-7628-4d1a-b36b-27fcea98b4ca.png"></media:content><category>python</category><tt:hashtag>logger</tt:hashtag><tt:hashtag>logging</tt:hashtag><tt:hashtag>activitylogger</tt:hashtag><description><![CDATA[<img src="https://img4.teletype.in/files/73/ef/73ef3893-6c76-4581-85fb-191810bdb2b0.png"></img>Логируй так, будто завтра сервер упадет, и только твои записи спасут мир.]]></description><content:encoded><![CDATA[
  <figure id="3tNA" class="m_column">
    <img src="https://img4.teletype.in/files/73/ef/73ef3893-6c76-4581-85fb-191810bdb2b0.png" width="763" />
  </figure>
  <p id="Ck3A"><strong>Логируй так, будто завтра сервер упадет, и только твои записи спасут мир.</strong></p>
  <p id="eQFG">Рассмотрим, как создать логгер для проекта, который предусматривает вывод сообщений и в консоль, и в файл журнала. Реализовывать его будем в форме класса <strong>Python</strong>, но сначала рассмотрим типичные особенности в интерактивном режиме. </p>
  <p id="lcF8">Нашими строительными блоками будут составляющие модуля <strong>logging</strong>:</p>
  <ul id="YEbJ">
    <li id="eZYP">функция <strong>getLogger </strong>для получения логгера с заданным именем;</li>
    <li id="I7l0">классы handler-ов: <strong>FileHandler </strong>для логирования в файл, <strong>StreamHandler </strong>для вывода в консоль, которые добавляются в полученный на предыдущем этапе логгер;</li>
    <li id="RR6F">метод логгера <strong>setLevel </strong>для задания уровня вывода сообщений;</li>
    <li id="NfkZ">методы логгера <strong>info</strong>, <strong>debug</strong>, <strong>error</strong>, <strong>warning </strong>для вывода сообщений с заданным уровнем.</li>
  </ul>
  <p id="p6vZ">Про уровни вывода сообщений и методы я рассказывал <a href="https://www.google.com/url?q=https%3A%2F%2Fdzen.ru%2Fa%2FYw3sJ7bEuy5UxE8w" target="_blank">ранее</a>. Создадим тестовый logger:</p>
  <pre id="26Bh" data-lang="python">import logging

file_handler = logging.FileHandler(&#x27;journal.log&#x27;)
console_handler = logging.StreamHandler()

logger = logging.getLogger(&#x27;test_logger&#x27;)
logger.setLevel(logging.INFO)

logger.addHandler(file_handler)
logger.addHandler(console_handler)

logger.info(&#x27;test message 1&#x27;)</pre>
  <pre id="v8J1">!cat journal.log</pre>
  <figure id="DqXb" class="m_column">
    <img src="https://img3.teletype.in/files/2c/10/2c10637f-b7d7-4c56-9985-03ff97c95763.png" width="1044" />
  </figure>
  <h2 id="gs0P">propagate</h2>
  <p id="F7md">Можно заметить 2 сообщения в консоли и одно - в журнале. Почему в консоль отправлен лишний вывод? </p>
  <p id="ml28">Поведение по умолчанию предполагает передачу сообщений вверх по иерархии к root-логгеру, который является предком всех logger-ов и обрабатывает сообщение путем вывода на консоль. Это регулируется атрибутом <strong>propagate </strong>(который по умолчанию равен True). Соответственно, чтобы остановить передачу &quot;наверх&quot; надо задать: <strong>logger.propagate = False</strong>:</p>
  <pre id="qvUM" data-lang="python">logger.propagate = False
logger.info(&#x27;test message 2&#x27;)</pre>
  <figure id="z4w9" class="m_column">
    <img src="https://img4.teletype.in/files/fb/78/fb78d2c6-77a3-41f6-8315-099043b4ab79.png" width="1053" />
  </figure>
  <h2 id="qjoF">разные хэндлеры</h2>
  <p id="sxPF">Для вывода списка обработчиков, которые отвечают за отправку сообщений в канал, можно обратиться к свойству <strong>logger.handlers</strong>:</p>
  <pre id="YJZi" data-lang="python">logger.handlers</pre>
  <figure id="O2mE" class="m_column">
    <img src="https://img4.teletype.in/files/b0/62/b062aee5-ad00-490c-8758-3f75a44b0c00.png" width="1008" />
  </figure>
  <p id="WHEq">Если попытаться добавить одинаковые хэндлеры, то список не изменится:</p>
  <pre id="m4zl" data-lang="python">logger.addHandler(file_handler)
logger.addHandler(console_handler)
logger.handlers</pre>
  <figure id="2vUP" class="m_column">
    <img src="https://img3.teletype.in/files/6f/53/6f539460-cf2d-41a5-ba4c-4fe22f446ca1.png" width="1047" />
  </figure>
  <p id="hEsY">Однако при создании новых объектов (и с другими id, соответственно) система будет считать их другими и произойдет дублирование вывода, поэтому при двухкратном обращении к одному логгеру в нашем классе надо будет предусмотреть такое поведение:</p>
  <pre id="ypop">file_handler = logging.FileHandler(&#x27;journal.log&#x27;)
console_handler = logging.StreamHandler()

logger.addHandler(file_handler)
logger.addHandler(console_handler)
logger.handlers</pre>
  <figure id="VXe7" class="m_column">
    <img src="https://img2.teletype.in/files/de/41/de41b10f-ff1f-4908-9616-93cc0a769410.png" width="1026" />
  </figure>
  <figure id="SlHz" class="m_column">
    <img src="https://img1.teletype.in/files/80/e7/80e7f280-a9c4-4279-a952-354b2ef76afd.png" width="1036" />
  </figure>
  <h2 id="LXU9">закрытие хэндлеров</h2>
  <hr />
  <p id="ZCZz">По завершении работы с логгером надо удалить из него привязку к обработчикам, что можно сделать, очистив соответствующий список. Однако следует помнить о коварной ошибке при удалении handler-ов из того же списка, по которому происходит итерация:</p>
  <pre id="eh5X" data-lang="python">for handler in logger.handlers:
    logger.removeHandler(handler)

logger.handlers</pre>
  <figure id="JaAQ" class="m_column">
    <img src="https://img3.teletype.in/files/e9/6e/e96ecf0d-db44-46bf-9e79-726e06ed81da.png" width="1009" />
  </figure>
  <p id="eX2O">А так (logger.handlers[:]) создаем копию списка для итерации, поэтому результат будет пустым, и новые сообщения при обращении к логгеру не появляются:</p>
  <pre id="H01r" data-lang="python">for handler in logger.handlers[:]:
    logger.removeHandler(handler)
logger.handlers</pre>
  <figure id="3r2S" class="m_column">
    <img src="https://img1.teletype.in/files/81/d8/81d85947-56e6-4bf9-95d1-4855353ab71f.png" width="1042" />
  </figure>
  <p id="u8OJ">Для корректного освобождения ресурсов (закрытия файла, сброса буферов) также рекомендуется вызывать метод <strong>close </strong>обработчиков:</p>
  <pre id="xnjT" data-lang="python">file_handler.stream.closed</pre>
  <pre id="Vc8N">file_handler.close()
console_handler.close()
</pre>
  <figure id="gEEG" class="m_column">
    <img src="https://img2.teletype.in/files/10/e4/10e4142b-4c4f-44be-9a28-0e062831b211.png" width="1037" />
  </figure>
  <h2 id="hq3J">класс логгера</h2>
  <p id="5Lbp">На основании описанного выше можно создать класс логгера, который одновременно выводит и в журнал, и на консоль. Также добавим удобный формат сообщений (подробнее было <a href="true">тут</a>).</p>
  <pre id="jRGR" data-lang="python">import logging

LOG_FN = &#x27;journal.log&#x27;

class ActivityLogger():

    def __init__(self, level=logging.INFO, encoding=&#x27;utf-8&#x27;):

        self.level = level
        self.file_handler = logging.FileHandler(LOG_FN, encoding=encoding)
        self.console_handler = logging.StreamHandler()

    def get_logger(self, name):

        file_formatter = logging.Formatter(f&#x27;%(asctime)s \nMODULE:{name}, LEVEL:%(levelname)s, LINE:%(lineno)s, MSG:%(message)s\n&#x27;)
        cons_formatter = logging.Formatter(f&#x27;%(asctime)s \n%(lineno)s %(message)s\n\n&#x27;)

        # Применяем форматтер к обоим Handler
        self.file_handler.setFormatter(file_formatter)
        self.console_handler.setFormatter(cons_formatter)

        # Создаем объект logger и применяем к нему оба Handler
        logger = logging.getLogger(name)
        logger.setLevel(self.level)

        if len(logger.handlers)==0:
            logger.addHandler(self.file_handler)
            logger.addHandler(self.console_handler)

        # Disable propagation of messages to the root logger
        logger.propagate = False

        return logger

    def close_logger(self, logger):

        # import pdb;pdb.set_trace()
        for handler in logger.handlers[:]:
            logger.removeHandler(handler)

        self.file_handler.close()
        self.console_handler.close()
</pre>
  <p id="UBQp">Для демонстрационных целей имитируем наличие проекта и класс logger-а в отдельном модуле:</p>
  <figure id="Enol" class="m_column">
    <img src="https://img2.teletype.in/files/9d/c4/9dc40c99-66e8-41d2-b1d5-89f100f41ff6.png" width="1057" />
  </figure>
  <p id="6quK"></p>
  <figure id="Q5Pr" class="m_column">
    <img src="https://img4.teletype.in/files/34/ca/34ca02ba-bfa2-4c80-a81c-9cad6fbeb2e4.png" width="1005" />
  </figure>
  <figure id="Qe1i" class="m_column">
    <img src="https://img3.teletype.in/files/2d/96/2d96566a-01e3-4d46-a19d-2413ecdda068.png" width="1046" />
  </figure>
  <h3 id="ek6R">скрипты передают свои имена в журнал</h3>
  <p id="gK4i">Теперь создадим 2 одинаковых скрипта, использующих логгер:</p>
  <pre id="X0Pu" data-lang="python">t = r&#x27;&#x27;&#x27;
from constants import ActivityLogger

try:

    logger = ActivityLogger().get_logger(__file__)
    logger.info(f&#x27;тестовое сообщение&#x27;)

    ActivityLogger().close_logger(logger)

except Exception:
    logger.exception(&#x27;ВОЗНИКЛО ИСКЛЮЧЕНИЕ!!!&#x27;)
    ActivityLogger().close_logger(logger)

&#x27;&#x27;&#x27;

with open(&#x27;project/script1.py&#x27;, &#x27;wt&#x27;) as f:
    f.write(t)

with open(&#x27;project/script2.py&#x27;, &#x27;wt&#x27;) as f:
    f.write(t)</pre>
  <figure id="A8E5" class="m_column">
    <img src="https://img1.teletype.in/files/0f/aa/0faa7ef5-ef2c-44c1-ae6e-53689c53a9ea.png" width="1013" />
  </figure>
  <p id="aniN">Запустим оба скрипта и увидим, что сообщения корректно выводятся и в журнал и на консоль с указанием файла-источника, что удобно для отладки:</p>
  <figure id="ARlv" class="m_column">
    <img src="https://img4.teletype.in/files/3d/a5/3da50e26-b7c7-406d-993f-238c719d74e1.png" width="1043" />
  </figure>
  <p id="HUxh"></p>
  <h3 id="DWfM">нет дублирования при многократном обращении к логгеру </h3>
  <p id="ptI7">Так как мы предусмотрели проверку наличия обработчиков (в блоке - if len...), при обращении к одному и тому же логгеру дважды сообщения не дублируются: </p>
  <pre id="WVJA" data-lang="python">from project.constants import ActivityLogger
import logging

logger1 = ActivityLogger(level = logging.ERROR).get_logger(&#x27;note1&#x27;)
logger1.error(&quot;тестовое сообщение note 1&quot;)

print(logging.getLogger(&#x27;note1&#x27;).handlers)</pre>
  <figure id="L7wD" class="m_column">
    <img src="https://img2.teletype.in/files/1c/72/1c72da12-0bf7-47b3-96b9-77f57a03f7bf.png" width="1078" />
  </figure>
  <pre id="yjdS" data-lang="python">logger2 = ActivityLogger().get_logger(&#x27;note1&#x27;)

logger2.error(&quot;тестовое сообщение note 2&quot;)
print(logger2.handlers)</pre>
  <figure id="vXVi" class="m_column">
    <img src="https://img3.teletype.in/files/e7/ff/e7ffb3d3-5ad7-4be9-bf7c-a1102e49dab1.png" width="1046" />
  </figure>
  <figure id="cwY8" class="m_column">
    <img src="https://img3.teletype.in/files/aa/ba/aabac9eb-1c11-4884-a628-3103c8569b9d.png" width="1044" />
  </figure>
  <p id="XGo1">Следует отметить, что вместо проверки наличия в логгере обработчиков, можно было бы задать их на уровне переменных класса (а не экземпляра), тогда они бы при повторном добавлении id не меняли и, соответственно, каналы бы не дублировались:</p>
  <pre id="tb80" data-lang="python">import logging

LOG_FN = &#x27;journal.log&#x27;

class ActivityLogger():

    file_handler = logging.FileHandler(LOG_FN, encoding=&#x27;utf-8&#x27;)
    console_handler = logging.StreamHandler()

    def __init__(self, level=logging.INFO):
        self.level = level

    def get_logger(self, name):

        file_formatter = logging.Formatter(f&#x27;%(asctime)s \nMODULE:{name}, LEVEL:%(levelname)s, LINE:%(lineno)s, MSG:%(message)s\n&#x27;)
        cons_formatter = logging.Formatter(f&#x27;%(asctime)s \n%(lineno)s %(message)s\n\n&#x27;)

        # Применяем форматтер к обоим Handler
        self.file_handler.setFormatter(file_formatter)
        self.console_handler.setFormatter(cons_formatter)

        # Создаем объект logger и применяем к нему оба Handler
        logger = logging.getLogger(name)
        logger.setLevel(logging.INFO)

        logger.addHandler(self.file_handler)
        logger.addHandler(self.console_handler)

        # Disable propagation of messages to the root logger
        logger.propagate = False

        return logger

    def close_logger(self, logger):

        for handler in logger.handlers[:]:
            logger.removeHandler(handler)

        self.file_handler.close()
        self.console_handler.close()

</pre>
  <h2 id="HbOY">параллельная работа с несколькими логгерами</h2>
  <p id="JnYQ">Если мы работаем с двумя логгерами, то закрытие одного не влияет на работу другого:</p>
  <pre id="b81A" data-lang="python">from project.constants import ActivityLogger

logger1 = ActivityLogger().get_logger(&#x27;note1&#x27;)
logger1.info(&quot;тестовое сообщение note 1&quot;)

logger2 = ActivityLogger().get_logger(&#x27;note2&#x27;)
logger2.info(&quot;тестовое сообщение note 2&quot;)</pre>
  <figure id="yV5a" class="m_column">
    <img src="https://img1.teletype.in/files/ca/f7/caf7350f-094c-4c15-b50c-17c6fcded1ac.png" width="1045" />
  </figure>
  <figure id="8j7k" class="m_column">
    <img src="https://img1.teletype.in/files/82/17/8217ecb8-5801-4300-879e-99f958469884.png" width="1058" />
  </figure>
  <figure id="Tskm" class="m_column">
    <img src="https://img3.teletype.in/files/aa/09/aa09369f-5692-4883-b02f-ee97b69dc0e3.png" width="1036" />
  </figure>
  <figure id="oJ9k" class="m_column">
    <img src="https://img2.teletype.in/files/97/f3/97f31ed9-4679-4137-a79b-f7b9d227392f.png" width="1050" />
  </figure>
  <tt-tags id="BECw">
    <tt-tag name="logger">#logger</tt-tag>
    <tt-tag name="logging">#logging</tt-tag>
    <tt-tag name="activitylogger">#activitylogger</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dt_analytic/6NXbvm7_1G5</guid><link>https://teletype.in/@dt_analytic/6NXbvm7_1G5?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic</link><comments>https://teletype.in/@dt_analytic/6NXbvm7_1G5?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic#comments</comments><dc:creator>dt_analytic</dc:creator><title>Как настроить окружение с PyTorch и CUDA</title><pubDate>Mon, 05 May 2025 07:32:17 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/32/67/32676516-b6d4-4b1a-902b-3aef67a21f51.png"></media:content><category>python</category><tt:hashtag>pytorch</tt:hashtag><tt:hashtag>nn</tt:hashtag><tt:hashtag>venv</tt:hashtag><tt:hashtag>conda</tt:hashtag><description><![CDATA[<img src="https://img3.teletype.in/files/25/8c/258cf292-498c-4719-bad7-93f849d827c7.png"></img>&quot;Путь к успеху начинается с первого шага&quot;. Даже для опытных пользователей процесс настройки окружения с PyTorch и CUDA может превратиться в настоящую головную боль, если нет четкого алгоритма. Поэтому описание порядка установки станет в центре внимания в данном материале .]]></description><content:encoded><![CDATA[
  <figure id="7n92" class="m_column">
    <img src="https://img3.teletype.in/files/25/8c/258cf292-498c-4719-bad7-93f849d827c7.png" width="1233" />
  </figure>
  <p id="DLJA">&quot;<strong>Путь к успеху начинается с первого шага</strong>&quot;. Даже для опытных пользователей процесс настройки окружения с <strong>PyTorch</strong> и <strong>CUDA </strong>может превратиться в настоящую головную боль, если нет четкого алгоритма. Поэтому описание порядка установки станет в центре внимания в данном материале .</p>
  <p id="EzAh">Первый этап - сбор информации о видеокарте, поддерживаемой версии CUDA и ее установка (все этапы тестировал на Windows). </p>
  <p id="V3Xu">1) Инфо о видеокарте можно получить через браузер <strong>Chrome</strong>:</p>
  <pre id="2aOD">chrome://gpu/</pre>
  <figure id="Lcka" class="m_column">
    <img src="https://img3.teletype.in/files/a6/8c/a68c7a39-36e1-4013-845c-e2cf4cac3708.png" width="1272" />
  </figure>
  <p id="rC9H">2) Далее гуглим, какая <strong>версия CUDA</strong> поддерживается вашей видеокартой, и скачиваем с официального сайта (в моем случае - CUDA 11.8 взял <a href="https://developer.nvidia.com/cuda-11-8-0-download-archive" target="_blank">тут</a>) и устанавливаем. О подробностях инсталляции и <strong>CUDA </strong>можно почитать <a href="https://docs.nvidia.com/cuda/cuda-installation-guide-microsoft-windows/index.html" target="_blank">на том же ресурсе</a> (в целом процесс не отличается от установки обычной программы).</p>
  <p id="DvUN">3) Если не прописана переменная среды <strong>CUDA_PATH</strong>, то создаем ее с указанием на папку с <strong>CUDA</strong>:</p>
  <p id="KGcG"></p>
  <figure id="QcEN" class="m_column">
    <img src="https://img4.teletype.in/files/30/21/30216441-5680-44d8-989d-1a0e4efc8a92.png" width="1298" />
  </figure>
  <p id="mLeB">4) Проверка </p>
  <p id="Whk4">инфо о <strong>CUDA</strong>:</p>
  <pre id="SJA7"> nvcc -V</pre>
  <p id="vLOL">+ мониторинг использования ГПУ:</p>
  <pre id="ThJQ">nvidia-smi</pre>
  <figure id="O3UG" class="m_column">
    <img src="https://img4.teletype.in/files/7b/53/7b5314b8-fd5e-4ff3-a545-012ca3a0e88f.png" width="1095" />
  </figure>
  <p id="IjRG">5) Далее создаем виртуальное окружение (я делаю это с <strong>conda</strong>):</p>
  <pre id="KoQi">conda create -y --prefix D:/dev/envs/pytorch_env python=3.10</pre>
  <figure id="PhoX" class="m_column">
    <img src="https://img2.teletype.in/files/1a/59/1a5939f4-d4f7-4b44-8fd2-337b31dee9ff.png" width="1119" />
  </figure>
  <p id="hE7O">6) На сайте <strong>PyTorch</strong> берем команды инсталляции пакета и исполняем их:</p>
  <p id="RpAH"><a href="https://pytorch.org/get-started/locally/" target="_blank">для самой свежей версии</a></p>
  <p id="gWtr"><a href="https://pytorch.org/get-started/previous-versions/" target="_blank">для более старых + установка с conda</a> </p>
  <p id="sfNw">В моем случае сработали команды:</p>
  <p id="nylY">для cuda 11.8:</p>
  <pre id="ZatQ">conda install pytorch==2.3.0 torchvision==0.18.0 torchaudio==2.3.0 pytorch-cuda=11.8 -c pytorch -c nvidia</pre>
  <p id="y7kp">для cuda 10.1:</p>
  <pre id="jqum">pip install torch==1.8.1+cu101 torchvision==0.9.1+cu101torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html</pre>
  <pre id="96US">conda install pytorch torchvision torchaudio cudatoolkit=10.1 -c pytorch</pre>
  <p id="jVNJ"></p>
  <figure id="9YaG" class="m_column">
    <img src="https://img4.teletype.in/files/fd/4b/fd4b8827-4ae6-43d7-9e09-976a7e1337e3.png" width="1108" />
  </figure>
  <p id="05Lb">...</p>
  <figure id="Cyff" class="m_column">
    <img src="https://img3.teletype.in/files/ab/a2/aba2f3ac-a6d4-400f-90e8-b701b31b78ae.png" width="1075" />
  </figure>
  <p id="TH8a">7) Проверяем доступность <strong>CUDA</strong>:</p>
  <pre id="no4m">python -c &quot;import torch;print(torch.cuda.is_available())&quot;</pre>
  <figure id="iKq1" class="m_column">
    <img src="https://img3.teletype.in/files/62/64/6264de83-5325-43c2-8644-1ca0a4863c12.png" width="1063" />
  </figure>
  <p id="QXDF">8) Напоминаю команды создания kernel-а из действующего окружения для jupyter (подробнее <a href="https://dzen.ru/a/Yuv0Q1xRhkeqdtUx" target="_blank">писал тут</a>):</p>
  <pre id="DIfA">pip install ipykernel</pre>
  <pre id="pFuS">python -m ipykernel install --user --name имя_среды </pre>
  <p id="BAOf"></p>
  <p id="LRVL">Вот как выглядит результат <strong>nvidia-smi</strong> во время работы скрипта, использующего ГПУ:</p>
  <figure id="mBtb" class="m_column">
    <img src="https://img3.teletype.in/files/2d/dc/2ddc664a-3271-4259-9b36-443e937a75a6.png" width="1045" />
  </figure>
  <tt-tags id="o0eY">
    <tt-tag name="pytorch">#pytorch</tt-tag>
    <tt-tag name="nn">#nn</tt-tag>
    <tt-tag name="venv">#venv</tt-tag>
    <tt-tag name="conda">#conda</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dt_analytic/HGtFYGEUMni</guid><link>https://teletype.in/@dt_analytic/HGtFYGEUMni?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic</link><comments>https://teletype.in/@dt_analytic/HGtFYGEUMni?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic#comments</comments><dc:creator>dt_analytic</dc:creator><title>Модуль inspect и самый частый код разработчика, который можно упростить до пары строк</title><pubDate>Sat, 22 Feb 2025 13:24:11 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/aa/f8/aaf8f709-13cf-49f6-be83-abadc8cfbc0d.png"></media:content><category>python</category><tt:hashtag>python</tt:hashtag><tt:hashtag>inspect</tt:hashtag><tt:hashtag>store_attr</tt:hashtag><tt:hashtag>frame</tt:hashtag><tt:hashtag>getmodule</tt:hashtag><tt:hashtag>copy_attr</tt:hashtag><tt:hashtag>copy_args</tt:hashtag><tt:hashtag>debug</tt:hashtag><description><![CDATA[<img src="https://img3.teletype.in/files/e8/bc/e8bc7897-4c60-4eba-9c3f-d9a4fe9621ef.png"></img>Превратите сложные задачи в простые: узнайте, как модуль inspect помогает разработчикам отлаживать и лучше понимать чужой код, а также сокращать время, затрачиваемое на рутинные действия.]]></description><content:encoded><![CDATA[
  <figure id="CYKm" class="m_column">
    <img src="https://img3.teletype.in/files/e8/bc/e8bc7897-4c60-4eba-9c3f-d9a4fe9621ef.png" width="869" />
  </figure>
  <p id="sANf">Превратите сложные задачи в простые: узнайте, как модуль <strong>inspect </strong>помогает разработчикам отлаживать и лучше понимать чужой код, а также сокращать время, затрачиваемое на рутинные действия.</p>
  <p id="5DFi">Начнем с описания самых простых и полезных функций модуля. </p>
  <h2 id="Odc9">getmodule и getfile</h2>
  <p id="xS9h"><strong>getmodule </strong>и <strong>getfile </strong>позволяют получить имя модуля и файла, в которых определен импортированный объект:</p>
  <pre id="rfgA" data-lang="python">import inspect
from fastai.vision.all import *

inspect.getmodule(L)</pre>
  <pre id="I5Ge">inspect.getfile(L)</pre>
  <figure id="w1bn" class="m_column">
    <img src="https://img3.teletype.in/files/e2/bb/e2bb4638-24ac-47f1-b266-6ca350b03ed7.png" width="786" />
  </figure>
  <h2 id="qOQl">function signature</h2>
  <hr />
  <p id="7W5I">Для получения описания параметров и значений по умолчанию исследуемой функции обратитесь к <strong>signature</strong>:</p>
  <pre id="NkjG" data-lang="python">def f(a, b:list=[2]):
  pass

sig = inspect.signature(f)

for name, param in sig.parameters.items():
    print(f&quot;Parameter: {name}&quot;)
    print(f&quot;  Default: {param.default}&quot;)
    print(f&quot;  Annotation: {param.annotation}&quot;)
    print(f&quot;  Kind: {param.kind}&quot;)
    print()</pre>
  <figure id="4Pxh" class="m_column">
    <img src="https://img2.teletype.in/files/9a/9a/9a9a440f-c8c7-42d6-9bb5-186607c20e89.png" width="670" />
  </figure>
  <h2 id="3hZ0">frame</h2>
  <hr />
  <p id="TaEf">Пожалуй, самым полезным объектом модуля является фрейм/блок. Функция <strong>currentframe </strong>возвращает ссылку на текущий блок кода, у которого есть следующие важные атрибуты:</p>
  <ul id="HqkM">
    <li id="ZLTw"><strong>f_locals </strong>- словарь локальных переменных</li>
    <li id="t4b7"><strong>f_globals </strong>- словарь глобальных переменных</li>
    <li id="y7iI"><strong>f_back </strong>- позволяет обратиться к фрейму на уровень выше</li>
    <li id="Iizz"><strong>f_code </strong>- объект кода, привязанный к фрейму, как правило, описывает функцию, в которой происходит выполнение блока. В свою очередь, имеет атрибут <strong>co_varnames</strong>, содержащий имена аргументов и локальных переменных;</li>
  </ul>
  <p id="XSQa">Ниже демонстрируются эти свойства фреймов:</p>
  <pre id="gQ9r" data-lang="python">def f2(a=1):
  frame = inspect.currentframe()
  print(f&#x27;f2 local vars - {(frame.f_locals.items())}&#x27;)
  print(f&#x27;f2 func vars and locals - {frame.f_code.co_varnames}&#x27;)
  print(f&#x27;f1 func vars and locals - {frame.f_back.f_code.co_varnames}&#x27;)
  print(f&#x27;f0 func vars and locals - {frame.f_back.f_back.f_code.co_varnames}&#x27;)

def f1(b):
  f2()

def f0(c=2):
  f1(2)

f0()</pre>
  <figure id="HoLS" class="m_column">
    <img src="https://img2.teletype.in/files/d3/8d/d38d08af-9029-4037-a4cf-aa73ec1295ad.png" width="749" />
  </figure>
  <h2 id="Lr9W"><strong>store_attr </strong></h2>
  <p id="0gn9">Представленные выше свойства можно использовать для автоматизации присвоения внутренним атрибутам значений переменных, передаваемых в конструкторе класса, вида:</p>
  <p id="dq9L">self.a=a</p>
  <p id="3alq">...</p>
  <p id="gHYB">Этот шаблонный код, пожалуй, писал каждый питонист хотя бы раз.</p>
  <pre id="QKaN" data-lang="python">class C():

    def __init__(self, a, b):
        frame = inspect.currentframe()
        for k, v in frame.f_locals.items():
          setattr(self, k, v)

item = C(a=3, b=[1, 3])
item.a, item.b</pre>
  <figure id="8CZc" class="m_column">
    <img src="https://img1.teletype.in/files/c8/c2/c8c2d14a-ccf6-495e-95f0-86fe8700e582.png" width="723" />
  </figure>
  <p id="YSAS">Если делать то же через функцию, то понадобится обратиться к вышестоящему фрейму:</p>
  <pre id="aaIo" data-lang="python">def copy_args():

    frame = inspect.currentframe().f_back
    code = frame.f_code
    args = code.co_varnames

    self = frame.f_locals[args[0]]
    for k, v in frame.f_locals.items():
      setattr(self, k, v)

class C():
    def __init__(self, a, b):
        copy_args()

item = C(a=3, b=[1, 3])
item.a, item.b</pre>
  <figure id="Fma1" class="m_column">
    <img src="https://img4.teletype.in/files/7a/3e/7a3eb212-7ed6-4835-a94b-354551293d83.png" width="719" />
  </figure>
  <p id="6nFy">Схожий функционал заложен в функции <strong>store_attr </strong>из модуля <strong>fastcore.basics</strong>:</p>
  <pre id="yB1F" data-lang="python">from fastcore.basics import store_attr

class C():
    def __init__(self, a, b):
        store_attr()

item = C(a=3, b=[1, 3])
item.a, item.b</pre>
  <figure id="TwIi" class="m_column">
    <img src="https://img1.teletype.in/files/06/25/06258e89-cd04-4cc7-be59-2a44f9280262.png" width="728" />
  </figure>
  <p id="WAlQ">Можно часть параметров сохранить через <strong>store_attr</strong>,перечислив их в скобках через запятую, а часть определить самим:</p>
  <pre id="wKXx" data-lang="python">from fastcore.basics import store_attr

class C():
    def __init__(self, a, b, c):
        store_attr(&#x27;a, c&#x27;)
        self.b = a+c

item = C(a=3, b=3, c=5)
item.a, item.b, item.c</pre>
  <h2 id="Abla"><strong>stack </strong></h2>
  <p id="qf0H">Еще посредством функции <strong>stack </strong>модуля <strong>inspect </strong>из произвольной строки можно получить информацию о стеке вызовов по аналогии с тем, который выводится при ошибке. Пусть у нас есть модуль:</p>
  <figure id="BzMd" class="m_column">
    <img src="https://img3.teletype.in/files/6a/19/6a19dff4-7922-499d-8898-f783bb89b675.png" width="799" />
  </figure>
  <p id="Zhzn">После вызова f0 мы получим информацию о номере строки текущего фрейма (<strong>lineno</strong>), функции (<strong>function</strong>) и файле, в котором она находится (<strong>filename</strong>). Кроме того, ниже из стека выводится информация о служебных инструментах, которые используются collab-ом:</p>
  <pre id="Uib4" data-lang="python">import sys
sys.path.append(&#x27;/content/drive/MyDrive/Colab Notebooks&#x27;)
from my_funcs.module import f0

f0()</pre>
  <figure id="wxXE" class="m_column">
    <img src="https://img1.teletype.in/files/0d/22/0d223e00-338a-43eb-a039-614593807c23.png" width="854" />
  </figure>
  <tt-tags id="dhKC">
    <tt-tag name="python">#python</tt-tag>
    <tt-tag name="inspect">#inspect</tt-tag>
    <tt-tag name="store_attr">#store_attr</tt-tag>
    <tt-tag name="frame">#frame</tt-tag>
    <tt-tag name="getmodule">#getmodule</tt-tag>
    <tt-tag name="copy_attr">#copy_attr</tt-tag>
    <tt-tag name="copy_args">#copy_args</tt-tag>
    <tt-tag name="debug">#debug</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dt_analytic/mzSTJ5hRZnn</guid><link>https://teletype.in/@dt_analytic/mzSTJ5hRZnn?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic</link><comments>https://teletype.in/@dt_analytic/mzSTJ5hRZnn?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic#comments</comments><dc:creator>dt_analytic</dc:creator><title>Защита реквизитов с Python</title><pubDate>Thu, 17 Oct 2024 13:13:17 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/d8/30/d830ebbf-3f08-4136-becd-a1c6bec84c44.png"></media:content><category>python</category><tt:hashtag>secret</tt:hashtag><tt:hashtag>python</tt:hashtag><tt:hashtag>system</tt:hashtag><tt:hashtag>credentials</tt:hashtag><description><![CDATA[<img src="https://img2.teletype.in/files/d2/06/d206bbd7-8921-47e1-af68-892b05b80284.png"></img>&quot;Конфиденциальность — это не только право, это основа свободы.&quot; В эпоху цифровых технологий, когда данные становятся всё более ценным ресурсом, защита личной информации приобретает критическое значение. В этой статье я расскажу, как работать с конфиденциальными реквизитами в Python проекте.]]></description><content:encoded><![CDATA[
  <figure id="V7nw" class="m_column">
    <img src="https://img2.teletype.in/files/d2/06/d206bbd7-8921-47e1-af68-892b05b80284.png" width="854" />
  </figure>
  <p id="tbCd">&quot;Конфиденциальность — это не только право, это основа свободы.&quot; В эпоху цифровых технологий, когда данные становятся всё более ценным ресурсом, защита личной информации приобретает критическое значение. В этой статье я расскажу, как работать с конфиденциальными реквизитами в Python проекте.</p>
  <h1 id="jyWT">load_dotenv</h1>
  <hr />
  <p id="FDAc">Первый способ - использовать функцию <strong>load_dotenv </strong>из модуля <strong>dotenv</strong>. Она позволяет считать строки вида ключ=значение из файла и загрузить их в переменные окружения с именем ключа. Обычно таким образом задаются конфиденциальные данные, как пароли, ключи API, которые не следует хранить в коде и тем более заливать в репозиторий. </p>
  <p id="6pql">Отмечу, что этот способ подойдет, если злоумышленник не имеет доступ к локальным файлам, так как пароль хранится в открытом виде. Продемонстрируем работу на практике. Сначала установим модуль <strong>python-dotenv </strong>и создадим файл:</p>
  <pre id="KqHd" data-lang="python">!pip install python-dotenv -q

!mkdir pers
!printf &#x27;&#x27;&#x27; USER=&quot;user&quot;\n\
PSWD=&quot;1pswd1&quot; &#x27;&#x27;&#x27; &gt; pers/.env

!cat .env</pre>
  <figure id="1BZZ" class="m_column">
    <img src="https://img4.teletype.in/files/3b/21/3b21243b-db77-4277-9450-665bfc8bc483.png" width="1096" />
  </figure>
  <p id="L9q1">Теперь вызовем <strong>load_dotenv </strong>с указанием пути к файлу:</p>
  <pre id="oJCQ" data-lang="python">from dotenv import load_dotenv

load_dotenv(&#x27;pers/.env&#x27;)</pre>
  <figure id="8HDp" class="m_column">
    <img src="https://img1.teletype.in/files/04/f6/04f60c42-e1bd-4eec-b4f9-7f66a166661c.png" width="1109" />
  </figure>
  <p id="dqnB">После этого переменные из файла доступны через окружение:</p>
  <pre id="Q1EF" data-lang="python">import os
os.environ[&#x27;USER&#x27;], os.environ[&#x27;PSWD&#x27;]</pre>
  <figure id="souw" class="m_column">
    <img src="https://img1.teletype.in/files/4c/9f/4c9f6527-405d-4eed-a916-fda2c0944ea2.png" width="1132" />
  </figure>
  <p id="HhXC">Если все равно страшно хранить реквизиты в открытом файле, можно немного усложнить и, например, хранить часть пароля в файле, а часть добавлять из кода (но это все равно не безопасное решение). В функции ниже прописано такое поведение - при отсутствии создается файл с реквизитами (лучше, чтобы каждый раз при старте сессии, после удалять), при этом первый и последний символы, пароля не хранятся в файле, а добавляются в коде:</p>
  <pre id="OWX1" data-lang="python">from getpass import getpass

def get_creds():

    if not os.path.exists(&#x27;pers/.env&#x27;):
        cred = getpass()
        with open(&#x27;pers/.env&#x27;, &#x27;wt&#x27;) as f_wr:
            f_wr.write(f&#x27;PSWD={cred[1:-1]}&#x27;)

    load_dotenv(&#x27;pers/.env&#x27;)
    PSWD = &#x27;1&#x27;+os.environ[&#x27;PSWD&#x27;]+&#x27;1&#x27;

    return PSWD return PSWD</pre>
  <figure id="CHKT" class="m_column">
    <img src="https://img1.teletype.in/files/cc/39/cc39b7ec-d781-47de-bf17-b89d5be06c28.png" width="1123" />
  </figure>
  <p id="NTEE">Удалим файл, старое значением переменной окружения и вызовем функцию:</p>
  <pre id="Pbnr">!rm pers/.env</pre>
  <pre id="Xl57" data-lang="python">del os.environ[&#x27;PSWD&#x27;]</pre>
  <pre id="arga" data-lang="python">USER = &#x27;user&#x27;
PSWD = get_creds()
PSWD</pre>
  <figure id="2Ghz" class="m_column">
    <img src="https://img4.teletype.in/files/7e/15/7e159b8c-80a3-438c-8e88-f69126aaddc5.png" width="1107" />
  </figure>
  <p id="hAbA">Пароль правильный и он не хранится в файле полностью:</p>
  <pre id="oqvH" data-lang="python">!cat pers/.env</pre>
  <figure id="lGxf" class="m_column">
    <img src="https://img1.teletype.in/files/c4/60/c460a41f-5ee6-437b-9e98-ebe61a24e56b.png" width="1104" />
  </figure>
  <p id="Iwnu">Если вы делаете проект, который будет запускать лицо с другими реквизитами (например, заказчик), удобно функцию <strong>get_creds </strong>не показывать, а объявить в отдельно модуле, который не попадет в репозиторий. При этом в отдельном общедоступном py файле прописать переменные <strong>USER = &#x27;user&#x27;, PSWD = get_creds()</strong>. Во время передачи проекта указать, что следует задать в переменной <strong>PSWD </strong>пароль для запуска или для большей безопасности прописать там <strong>getpass</strong>.</p>
  <h1 id="wuS2">keyring</h1>
  <hr />
  <p id="2Ktc">Когда существует опасность доступа злоумышленника к вашим файлам, надежным способом хранения реквизитов является их шифрование. Как вариант, можно воспользоваться модулем <strong>keyring</strong>. Сначала установим необходимые библиотеки:</p>
  <pre id="meNK">pip install keyring keyrings.alt pycryptodome -q</pre>
  <pre id="O3tC" data-lang="python">import keyring
from keyrings.alt.file import EncryptedKeyring</pre>
  <figure id="eYPF" class="m_column">
    <img src="https://img3.teletype.in/files/22/3c/223c6796-b1ae-4396-acc1-30c26d6b4c54.png" width="1120" />
  </figure>
  <p id="XvyY">Зададим тип хранилища:</p>
  <pre id="EpeG" data-lang="python">keyring.set_keyring(EncryptedKeyring())</pre>
  <figure id="mxzZ" class="m_column">
    <img src="https://img3.teletype.in/files/24/37/2437cc08-0763-4633-b145-9cdb8c215169.png" width="1120" />
  </figure>
  <p id="2BB2">Для записи или обновления пароля можно использовать функцию <strong>set_password </strong>модуля <strong>keyring</strong>. Первый параметр - имя сервиса (ключа), к которому привязан пароль:</p>
  <pre id="1cQS">keyring.set_password(&#x27;main&#x27;, &#x27;user&#x27;, &#x27;1pswd1&#x27;)</pre>
  <figure id="tmE5" class="m_column">
    <img src="https://img1.teletype.in/files/43/d2/43d23d8d-c619-4595-8dd4-6933e800e975.png" width="1140" />
  </figure>
  <p id="I34D">Для получения пароля необходимо указать имя сервиса и логин, а функция при первом ее вызове потребует ввод пароля хранилища, который задавался в <strong>set_password</strong>:</p>
  <pre id="kKF1" data-lang="python">keyring.get_password(&#x27;main&#x27;, &#x27;user&#x27;)</pre>
  <figure id="xVFW" class="m_column">
    <img src="https://img1.teletype.in/files/8b/dc/8bdc6d1c-428b-4f0c-9718-0ec326b23e55.png" width="1100" />
  </figure>
  <p id="MIpP">Для получения пути к файлу с зашифрованным паролем можно вызвать атрибут <strong>file_path </strong>класса <strong>EncryptedKeyring</strong>:</p>
  <pre id="GBNy" data-lang="python">EncryptedKeyring().file_path</pre>
  <pre id="NyVi">!cat /root/.local/share/python_keyring/crypted_pass.cfg</pre>
  <figure id="6YiL" class="m_column">
    <img src="https://img1.teletype.in/files/0b/da/0bda648a-1cd5-43f4-b2a8-b0a4a7115014.png" width="1110" />
  </figure>
  <p id="g9bb">Также есть и другие способы получения пути к файлу с реквизитами, которые в зависимости от системы могут сработать или нет:</p>
  <ol id="53Ai">
    <li id="ruFO"><strong>keyring.get_keyring()</strong> в ячейке</li>
    <li id="SghJ"><strong>keyring diagnose</strong> в терминале</li>
  </ol>
  <tt-tags id="9Vo0">
    <tt-tag name="secret">#secret</tt-tag>
    <tt-tag name="python">#python</tt-tag>
    <tt-tag name="system">#system</tt-tag>
    <tt-tag name="credentials">#credentials</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dt_analytic/crMH2h96eAh</guid><link>https://teletype.in/@dt_analytic/crMH2h96eAh?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic</link><comments>https://teletype.in/@dt_analytic/crMH2h96eAh?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic#comments</comments><dc:creator>dt_analytic</dc:creator><title>Визуализация матрицы расхождений - ключ к пониманию ошибок классификации</title><pubDate>Fri, 09 Aug 2024 13:58:55 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/53/b5/53b5a3ea-deee-4b5f-9ac2-140dfbacbdad.png"></media:content><category>визуализация</category><tt:hashtag>vis</tt:hashtag><tt:hashtag>draw</tt:hashtag><tt:hashtag>ml</tt:hashtag><tt:hashtag>evaluation</tt:hashtag><description><![CDATA[<img src="https://img1.teletype.in/files/01/74/017499ff-471c-4598-b417-ffad0b2bcda0.png"></img>«Ошибки — это наука, помогающая нам двигаться вперёд», — говорил Уильям Ченнинг. Визуализация - отличный инструмент, который помогает анализировать данные и выявлять закономерности.]]></description><content:encoded><![CDATA[
  <figure id="s3VO" class="m_column">
    <img src="https://img1.teletype.in/files/01/74/017499ff-471c-4598-b417-ffad0b2bcda0.png" width="761" />
  </figure>
  <p id="UDdD">«<strong>Ошибки — это наука, помогающая нам двигаться вперёд</strong>», — говорил Уильям Ченнинг. Визуализация - отличный инструмент, который помогает анализировать данные и выявлять закономерности. </p>
  <p id="oIKF">Рассмотрим удобный способ отображения в <strong>Python</strong> одной из метрик классификации под названием <strong>confusion matrix </strong>(на русский переводят по-разному - матрица ошибок, неточностей, расхождений или несоответствий).</p>
  <p id="QhGv">Сначала загрузим демонстрационный датасет.</p>
  <pre id="h6iI">import numpy as np
import pandas as pd

from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression

df = load_iris(as_frame=True)[&#x27;frame&#x27;]
d = {k:v for k, v in enumerate(load_iris().target_names)}
df[&#x27;target&#x27;] = df[&#x27;target&#x27;].map(d)
df = df.sample(frac=1, random_state=0)

df.head()</pre>
  <figure id="YpRG" class="m_column">
    <img src="https://img3.teletype.in/files/e6/5d/e65de6f3-461e-4a75-aa97-82014b775228.png" width="935" />
  </figure>
  <p id="lzlG">Имитируем обучение модели и предсказание, не проводя разбиения на выборки, так как наша цель - показать возможности для визуализации:</p>
  <pre id="jLXW">model = LogisticRegression(random_state=0, max_iter=1000).fit(df.drop(columns=&#x27;target&#x27;), df.target)

y_p = model.predict(df.drop(columns=&#x27;target&#x27;))</pre>
  <p id="69rK">Теперь получим значения ошибок, воспользовавшись функцией <strong>confusion_matrix </strong>из модуля <strong>sklearn.metrics</strong>:</p>
  <pre id="EKK1">from sklearn.metrics import confusion_matrix

labels = np.sort(df[&#x27;target&#x27;].unique())
cm = confusion_matrix(y_true = df[&#x27;target&#x27;], y_pred = y_p,
                      labels=labels,
                      normalize=None)
cm</pre>
  <figure id="0gHI" class="m_column">
    <img src="https://img1.teletype.in/files/ce/a1/cea1fd27-a32d-4d33-ad38-a073f8056f7c.png" width="890" />
  </figure>
  <p id="sbcT">В строке i и колонке j матрицы располагаются значения, соответствующие количеству объектов класса i, которые предсказаны как j. Значения классов берутся, как метки встретившиеся хотя бы раз в y_true или y_pred упорядоченные по возрастанию. Я намеренно передаю метки явно в параметре labels для демонстрации этого поведения (этим же параметром, можно поменять порядок вывода или указать подмножество/список меток для вывода).</p>
  <h2 id="L8EP">matshow</h2>
  <p id="5kPu">Визуализацию начнем с низкоуровневых способов <strong>matplotlib </strong>закончим более быстрыми. Первым кандидатом будет <strong>matshow </strong>модуля <strong>matplotlib.pyplot</strong>, которая предоставляет полотно, а элементы потребуется добавлять самостоятельно. Ниже показано, как задать размеры картинки, метки классов, а также дополнить ячейки значениями матрицы через текстовые элементы (большинство настроек разбирались ранее <a href="https://dzen.ru/a/YSXY1ljCwRwFy6Da" target="_blank">здесь</a> и<a href="https://dzen.ru/a/YRyXhDTPoGnRVDu4" target="_blank">здесь</a>):</p>
  <pre id="7iEQ">import matplotlib.pyplot as plt
fig = plt.figure(figsize=(5, 5))

plt.matshow(cm, alpha=1, cmap=&#x27;coolwarm&#x27;, fignum=fig.number)

plt.xticks(range(len(labels)), labels, rotation=&#x27;vertical&#x27;)
plt.yticks(range(len(labels)), labels)
plt.gca().xaxis.set_ticks_position(&#x27;bottom&#x27;)

for i in range(cm.shape[0]):
    for j in range(cm.shape[1]):
        plt.gca().text(x=j, y=i, s=cm[i][j], ha=&#x27;center&#x27;, va=&#x27;center&#x27;)

plt.xlabel(&#x27;Predicted label&#x27;)
plt.ylabel(&#x27;True label&#x27;)</pre>
  <figure id="KQ7X" class="m_column">
    <img src="https://img3.teletype.in/files/65/71/65712ad4-95c6-4ac7-8a50-5fa80c702af7.png" width="853" />
  </figure>
  <figure id="XVSQ" class="m_column">
    <img src="https://img1.teletype.in/files/4a/6f/4a6f8857-7046-4cf3-a67c-fad3debecfb0.png" width="861" />
  </figure>
  <h2 id="0YOB">imshow</h2>
  <hr />
  <p id="hKYn">Функция <strong>imshow</strong> работает аналогично, только для управления размером не надо явно передавать номер фигуры (как делали выше используя <strong>fignum</strong>,так как по умолчанию <strong>matshow</strong> сама создает фигуру):</p>
  <pre id="vJWM">fig = plt.figure(figsize=(5, 5))

plt.imshow(cm, cmap=&#x27;viridis&#x27;)

plt.xticks(range(len(labels)), labels, rotation=&#x27;vertical&#x27;)
plt.yticks(range(len(labels)), labels)
plt.gca().xaxis.set_ticks_position(&#x27;bottom&#x27;)

for i in range(cm.shape[0]):
    for j in range(cm.shape[1]):
        plt.gca().text(x=j, y=i, s=cm[i][j], ha=&#x27;center&#x27;, va=&#x27;center&#x27;, color=&quot;white&quot;)

plt.xlabel(&#x27;Predicted label&#x27;)
plt.ylabel(&#x27;True label&#x27;)</pre>
  <figure id="FfbG" class="m_column">
    <img src="https://img2.teletype.in/files/96/86/9686c66d-57ab-40e4-b9f1-9f32f11fb7af.png" width="858" />
  </figure>
  <figure id="wbcY" class="m_column">
    <img src="https://img1.teletype.in/files/4b/fb/4bfb5aaa-5d6f-4425-8a98-70d05dce663a.png" width="849" />
  </figure>
  <h2 id="SCDA">heatmap</h2>
  <p id="xotB">Для отображения с помощью функции <strong>heatmap </strong>из библиотеки <strong>seaborn </strong>надо будет внести минимальные правки (закомментировал строки, которые помогут добавить подписи):</p>
  <pre id="noRT">import seaborn as sns
plt.figure(figsize=(5,5))

# fmt=&#x27;.1f&#x27;
sns.heatmap(pd.DataFrame(cm, index=labels, columns=labels), annot=True,
            fmt=&#x27;d&#x27;, cmap=&#x27;inferno&#x27;, cbar=False)

# plt.xticks(range(len(labels)), list(labels), rotation=&#x27;vertical&#x27;)
# plt.yticks(range(len(labels)), labels)

# plt.xlabel(&#x27;Predicted label&#x27;)
# plt.ylabel(&#x27;True label&#x27;)</pre>
  <figure id="VYIP" class="m_column">
    <img src="https://img1.teletype.in/files/83/69/8369887a-a0e7-4479-837e-abda2ee52aab.png" width="836" />
  </figure>
  <figure id="IRyG" class="m_column">
    <img src="https://img3.teletype.in/files/ad/f2/adf223c0-a891-4091-851e-9534cc07f817.png" width="851" />
  </figure>
  <h2 id="ujrg">ConfusionMatrixDisplay</h2>
  <p id="47Z8">Об удобстве вывода задумались и разработчики <strong>scikit-learn</strong>, написав класс <strong>ConfusionMatrixDisplay</strong>. С помощью его методов, например, <strong>from_predictions </strong>можно сразу нарисовать картинку:</p>
  <pre id="VfjT">from sklearn.metrics import ConfusionMatrixDisplay


ConfusionMatrixDisplay.from_predictions(y_true=df[&#x27;target&#x27;], y_pred=y_p,
                                              labels=labels, colorbar=False,
                                              xticks_rotation=&#x27;vertical&#x27;, im_kw={&#x27;cmap&#x27;:&#x27;viridis&#x27;});

</pre>
  <figure id="NGCx" class="m_column">
    <img src="https://img4.teletype.in/files/b2/5e/b25e1ace-ce53-4abf-8af1-0e73be4534af.png" width="999" />
  </figure>
  <p id="Buef">Однако для более гибкой настройки лучше создать класс с помощью конструктора, инициализировав параметр <strong>confusion_matrix </strong>нашей матрицей ошибок, а затем вызвать метод <strong>plot </strong>объекта с настройками отображения:</p>
  <pre id="HhHY">cmp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=labels)

fig, ax = plt.subplots(figsize=(5,5))
cmp.plot(ax=ax, xticks_rotation=&#x27;vertical&#x27;, colorbar=False, im_kw={&#x27;cmap&#x27;:&#x27;coolwarm&#x27;},
         text_kw={&#x27;color&#x27;:&quot;white&quot;})
</pre>
  <figure id="HLSX" class="m_column">
    <img src="https://img1.teletype.in/files/cc/56/cc56a93b-08e8-4f8a-b0bb-454039c3f2d9.png" width="905" />
  </figure>
  <figure id="OmVD" class="m_column">
    <img src="https://img4.teletype.in/files/b6/24/b624df54-a9d2-43c5-baae-b8da7eea4863.png" width="835" />
  </figure>
  <tt-tags id="y44v">
    <tt-tag name="vis">#vis</tt-tag>
    <tt-tag name="draw">#draw</tt-tag>
    <tt-tag name="ml">#ml</tt-tag>
    <tt-tag name="evaluation">#evaluation</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dt_analytic/68Ux-rhNe1e</guid><link>https://teletype.in/@dt_analytic/68Ux-rhNe1e?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic</link><comments>https://teletype.in/@dt_analytic/68Ux-rhNe1e?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic#comments</comments><dc:creator>dt_analytic</dc:creator><title>Как создать бесплатную виртуальную машину и запустить микросервис за час </title><pubDate>Sat, 20 Jul 2024 02:59:45 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/fb/99/fb990f12-8ea3-41d9-aa9c-e2feefd50ced.png"></media:content><category>инструменты аналитика</category><tt:hashtag>servers</tt:hashtag><tt:hashtag>сбер</tt:hashtag><tt:hashtag>cloud_ru</tt:hashtag><tt:hashtag>сбероблако</tt:hashtag><tt:hashtag>jupyter</tt:hashtag><tt:hashtag>web</tt:hashtag><description><![CDATA[<img src="https://img2.teletype.in/files/18/42/1842b330-89b9-4f56-8052-31ad86324bd2.png"></img>&quot;Будущее уже здесь, просто оно еще не равномерно распределено&quot; (Уильям Гибсон). В наши дни будущее, где каждому можно получить собственную виртуальную машину и развернуть микросервис, уже наступило. Но все ли знают, как это сделать? В этом материале мы вместе пройдем по шагам, воспользовавшись сервисом cloud.ru.]]></description><content:encoded><![CDATA[
  <figure id="bfPq" class="m_column">
    <img src="https://img2.teletype.in/files/18/42/1842b330-89b9-4f56-8052-31ad86324bd2.png" width="853" />
  </figure>
  <p id="RPZT"><strong>&quot;Будущее уже здесь, просто оно еще не равномерно распределено&quot; (Уильям Гибсон)</strong>. В наши дни будущее, где каждому можно получить собственную виртуальную машину и развернуть микросервис, уже наступило. Но все ли знают, как это сделать? В этом материале мы вместе пройдем по шагам, воспользовавшись сервисом <a href="https://console.cloud.ru/" target="_blank">cloud.ru. </a></p>
  <h2 id="nFIE">Создание ключа доступа</h2>
  <p id="q1tu">Предварительно сгенерируем rsa ключи для входа, используя утилиту <strong>ssh-keygen</strong>:</p>
  <figure id="n0Rh" class="m_column">
    <img src="https://img4.teletype.in/files/72/83/72839261-f97a-490d-a579-c1acb8ee4a25.png" width="987" />
  </figure>
  <p id="ZEVR">Сгенерируется пара ключей rsa в домашнем каталоге:</p>
  <figure id="3cfK" class="m_column">
    <img src="https://img2.teletype.in/files/91/a7/91a7a2eb-7658-4337-b5bb-fe7dc4d4217d.png" width="946" />
  </figure>
  <p id="RcQC"></p>
  <h2 id="X2dY">Создание машины</h2>
  <p id="PlXF">Теперь надо перенести публичную часть (<strong>id_rsa.pub</strong>) на сервер. Логинимся удобным способом на  <a href="https://console.cloud.ru/" target="_blank">cloud.ru</a>, например, по сбер id. Кликаем в левом верхнем углу на прямоугольник с точками и выбираем опцию <strong>SSH-ключи</strong>:</p>
  <p id="S3iw"></p>
  <figure id="Na8I" class="m_column">
    <img src="https://img3.teletype.in/files/6e/c9/6ec9b3e6-d477-484a-ae7d-d1613f0f19c8.png" width="1043" />
  </figure>
  <p id="damr">Выбираем &quot;<strong>Создать ключ</strong>&quot;:</p>
  <figure id="Dd0Y" class="m_column">
    <img src="https://img2.teletype.in/files/10/74/1074abf3-54f3-45c9-8522-68e0b39f40ea.png" width="1800" />
  </figure>
  <p id="WHWD">Задаем имя, &quot;<strong>загружаем публичный ключ из файла</strong>&quot; и жмем создать: </p>
  <p id="OBKa"></p>
  <figure id="9LVa" class="m_column">
    <img src="https://img2.teletype.in/files/15/1a/151aeb75-cd02-4a43-9d41-603deb6d49be.png" width="1073" />
  </figure>
  <p id="k5fY"></p>
  <p id="LuVT"></p>
  <p id="fFrb">Затем кликаем на прямоугольник из точек в левом верхнем углу -&gt; инфраструктура-&gt; виртуальные машины:</p>
  <figure id="xlV3" class="m_column">
    <img src="https://img1.teletype.in/files/cf/c5/cfc5bbac-81fd-4812-8bda-50fd371d4b80.png" width="1204" />
  </figure>
  <p id="v6Op">Выбираем &quot;Создать виртуальную машину&quot;:</p>
  <figure id="2r3F" class="m_column">
    <img src="https://img1.teletype.in/files/41/ed/41ed9476-ce88-42e9-ac11-32fefd1f3a7b.png" width="1291" />
  </figure>
  <p id="eiPE">Переходим в опцию &quot;<strong>Получить ВМ бесплатно</strong>&quot; (на картинке в левом нижнем углу): </p>
  <figure id="ekVi" class="m_column">
    <img src="https://img4.teletype.in/files/3e/75/3e75d3b3-5692-4af5-84e5-e5db5ecc0756.png" width="1436" />
  </figure>
  <p id="G2vR"></p>
  <p id="ZnU9">Заполняем название, выбираем образ системы (я выбрал <strong>ubuntu</strong>), указываем имя пользователя и публичный ключ, который недавно создали. Также ставим галочку на &quot;<strong>Пароль</strong>&quot; и задаем его для входа через пароль (например, в виртуальном терминале в личном кабинете):</p>
  <figure id="8hrw" class="m_column">
    <img src="https://img2.teletype.in/files/17/dd/17ddad97-a875-4089-81ab-777ab7492056.png" width="889" />
  </figure>
  <p id="Rb4I"></p>
  <p id="pXaE">Можно сразу подключить публичный IP или потом, чтобы взаимосвязь с машиной происходила через Интернет (это платно, около 150 рублей в месяц, однако на 2 месяца у вас будет 4 тыс. рублей в качестве бонусов). В конце кликаем на &quot;<strong>Создать</strong>&quot; в самом низу:</p>
  <figure id="QsGX" class="m_column">
    <img src="https://img2.teletype.in/files/5b/18/5b183e40-910c-4804-ae09-a93db558053e.png" width="922" />
  </figure>
  <p id="aMwp">И ждем, когда машина будет создана:</p>
  <figure id="Dd31" class="m_column">
    <img src="https://img1.teletype.in/files/8f/02/8f0202ae-8094-4c6a-8881-7b4b0f7c0bc2.png" width="1800" />
  </figure>
  <p id="q9lF">Затем можно нажать на троеточие справа и перейти в виртуальную консоль:</p>
  <figure id="x1m1" class="m_column">
    <img src="https://img3.teletype.in/files/ea/43/ea4311ec-26dc-4dc3-841f-888e6b7d30df.png" width="1803" />
  </figure>
  <p id="RKz8">После ввода логина и пароля, вы получите доступ в терминал:</p>
  <figure id="6sJd" class="m_column">
    <img src="https://img3.teletype.in/files/6b/3b/6b3b4f23-0231-4026-994e-3e3cc33d8831.png" width="1040" />
  </figure>
  <p id="k7Vw">Видеоинструкцию по созданию машины можете посмотреть <a href="https://www.youtube.com/results?search_query=free+tier+sber+cloud+%D0%BF%D0%BE%D0%B4%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8+%D0%BA+%D0%B2%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5+%D0%BC%D0%B0%D1%88%D0%B8%D0%BD%D0%B5" target="_blank">здесь</a>.</p>
  <h2 id="kUcB">Внешний ip</h2>
  <p id="0n9n">Теперь давайте назначим публичный ip. Перейдите в опцию &quot;<strong>Виртуальные машины</strong>&quot; (по аналогии, как выше), нажмите на троеточие и выберите соответствующий пункт:</p>
  <p id="hugY"></p>
  <figure id="MUPJ" class="m_column">
    <img src="https://img2.teletype.in/files/dd/9f/dd9f6b6d-e894-466d-9755-f7d7c8665f07.png" width="1812" />
  </figure>
  <p id="DhgO">Выберите интерфейс из выпадающего списка и нажмите &quot;<strong>Назначить</strong>&quot;:</p>
  <p id="r34X"></p>
  <figure id="43Zm" class="m_original">
    <img src="https://img3.teletype.in/files/ec/3a/ec3a6444-3841-4738-8772-000dd4eef2a3.png" width="538" />
  </figure>
  <p id="D7V7"></p>
  <p id="5mK3">Теперь у нас есть ip, и можно для удобства зайти на виртуальную машину по ssh с локальной машины:</p>
  <pre id="ktHJ">ssh имя_пользователя@ip</pre>
  <p id="C10l">Так как мы настроили аутентификацию по rsa, пароль вводить не придется.</p>
  <h2 id="U1yf">Микросервис</h2>
  <h3 id="DRNF">установка docker</h3>
  <p id="6EPJ">Установим <strong>docker</strong>, набрав команды (версия для ubuntu):</p>
  <pre id="F5cn">sudo apt update
sudo apt install docker.io</pre>
  <p id="VrZl">Для проверки установки наберите команду:</p>
  <pre id="Yqdn">sudo systemctl start docker</pre>
  <p id="NjUv">После этого желательно добавить текущего пользователя в группу <strong>docker</strong>, чтобы команды данного инструмента выполнять без привилегий суперпользователя (<strong>sudo</strong>):</p>
  <pre id="46tk">sudo usermod -aG docker al</pre>
  <h3 id="vQFn">настройка правил для подключений</h3>
  <p id="iFdi">Перед запуском контейнера настроим правило для входящего трафика (<a href="https://cloud.ru/docs/security-groups/ug/topics/guides__add-sg-rule.html" target="_blank">тут подробнее</a>) от Jupyter lab. </p>
  <p id="34It">Опять жмем прямоугольник из точек, выбираем раздел &quot;<strong>Группы безопасности</strong>&quot;, нажимаем на группу:</p>
  <figure id="RMyX" class="m_column">
    <img src="https://img4.teletype.in/files/b2/be/b2be5f80-efe0-4a37-bdf0-4027c7048742.png" width="1804" />
  </figure>
  <p id="ItxH">Переходим во вкладку &quot;<strong>Правила</strong>&quot;, жмем &quot;<strong>Добавить правило</strong>&quot;:</p>
  <figure id="rgBE" class="m_column">
    <img src="https://img4.teletype.in/files/fb/71/fb711214-7e30-400c-979c-84863a003864.png" width="1328" />
  </figure>
  <p id="7RPP">и заполняем протокол, порт, ip адрес и маску (для разрешений всем - 0.0.0.0/0): </p>
  <figure id="uKsC" class="m_column">
    <img src="https://img3.teletype.in/files/64/23/64237938-e04c-40c2-aaf6-d467dea91ab2.png" width="737" />
  </figure>
  <p id="0te4">или так:</p>
  <figure id="jhde" class="m_original">
    <img src="https://img4.teletype.in/files/f6/13/f6134256-aec6-453d-b585-9655ab3ac1ac.png" width="570" />
  </figure>
  <p id="HXcN">Свой ip можно посмотреть <a href="https://2ip.ru" target="_blank">тут</a>. Для отдельных пользователей и малых офисов провайдер обычно использует динамические адреса и маску подсети /32, указывающую, что все биты адреса используются для идентификации конкретного устройства. Соответственно, из-за динамической природы адреса ваше правило для одиночного ip при следующем соединении может перестать работать.</p>
  <h3 id="Ublo">запуск сервиса</h3>
  <p id="uWH5">После этого перезагружаем машину, подключаемся к терминалу виртуальной машины и запускаем контейнер командой:</p>
  <pre id="aZL1">docker run -p 8888:8888 -p 4040:4040 -it --user root jupyter/pyspark-notebook start.sh jupyter lab</pre>
  <p id="NXUh">Затем на своей машине в браузере вводим:</p>
  <pre id="XaKe">http://ip:8888/lab?token=a5a6e2d32063017ea28ebe0ea1ab1d928eadea15d16cab50</pre>
  <p id="q65E">Напомню, что token высветится в логах запуска контейнера прямо в терминале.</p>
  <p id="1pYg">Теперь наслаждаемся результатом:</p>
  <figure id="HcYp" class="m_column">
    <img src="https://img4.teletype.in/files/b0/05/b005e957-da9e-4e7d-8933-0c452ecb1766.png" width="1616" />
  </figure>
  <tt-tags id="qDPK">
    <tt-tag name="servers">#servers</tt-tag>
    <tt-tag name="сбер">#сбер</tt-tag>
    <tt-tag name="cloud_ru">#cloud_ru</tt-tag>
    <tt-tag name="сбероблако">#сбероблако</tt-tag>
    <tt-tag name="jupyter">#jupyter</tt-tag>
    <tt-tag name="web">#web</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dt_analytic/VUXQ3Ssk8O8</guid><link>https://teletype.in/@dt_analytic/VUXQ3Ssk8O8?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic</link><comments>https://teletype.in/@dt_analytic/VUXQ3Ssk8O8?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic#comments</comments><dc:creator>dt_analytic</dc:creator><title>Визуализация ошибок, как навигатор к скрытым проблемам модели</title><pubDate>Sun, 12 May 2024 14:15:39 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/ad/81/ad81fa52-5477-45f5-9f2a-11eca642b3a4.png"></media:content><category>машинное обучение</category><tt:hashtag>ml</tt:hashtag><tt:hashtag>diag</tt:hashtag><tt:hashtag>ошибки</tt:hashtag><tt:hashtag>остатки</tt:hashtag><tt:hashtag>residuals</tt:hashtag><description><![CDATA[<img src="https://img3.teletype.in/files/ee/8a/ee8a892f-1d60-445f-b0b8-4b98a0482c95.png"></img>Визуализация — это язык, который позволяет нам видеть данные и понимать их смысл. Простой и эффективный способ диагностики результатов работы модели на различных объектах заключается в анализе разницы между прогнозами и целями. Он может показать, что в некоторых группах поведение модели имеет особенности (например, склонность к завышению или занижению прогнозов). Для демонстрации того, как строится такая визуализация загрузим набор данных:]]></description><content:encoded><![CDATA[
  <figure id="cr6j" class="m_column">
    <img src="https://img3.teletype.in/files/ee/8a/ee8a892f-1d60-445f-b0b8-4b98a0482c95.png" width="685" />
  </figure>
  <p id="1Ckm"><strong>Визуализация — это язык, который позволяет нам видеть данные и понимать их смысл</strong>. Простой и эффективный способ диагностики результатов работы модели на различных объектах заключается в анализе разницы между прогнозами и целями. Он может показать, что в некоторых группах поведение модели имеет особенности (например, склонность к завышению или занижению прогнозов). Для демонстрации того, как строится такая визуализация загрузим набор данных:</p>
  <pre id="u3e1" data-lang="python">from sklearn.datasets import load_diabetes
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(0)

df, y = load_diabetes(return_X_y=True, as_frame=True)
df[&#x27;target&#x27;] = y
display(df.head())
display(df.shape)</pre>
  <figure id="iDoA" class="m_column">
    <img src="https://img4.teletype.in/files/be/ff/beff0162-5e84-4e94-9e99-a5d1c1245ab3.png" width="733" />
  </figure>
  <p id="9Tmd">Разобьем датасет на две группы для обучения и оценки:</p>
  <pre id="jzJj" data-lang="python">from sklearn.model_selection import train_test_split

X_tr, X_ts, y_tr, y_ts = train_test_split(df.drop(columns=&#x27;target&#x27;).copy(),
                                          df[&#x27;target&#x27;], test_size=0.2)
y_tr.shape[0], y_ts.shape[0]</pre>
  <figure id="NFKD" class="m_column">
    <img src="https://img3.teletype.in/files/27/35/2735cf0d-87b6-48ec-9e20-3ca689204167.png" width="723" />
  </figure>
  <p id="wbRi">Теперь построим модель и сделаем предсказания:</p>
  <pre id="M64s" data-lang="python">from sklearn.linear_model import LinearRegression

model = LinearRegression()

model.fit(X_tr, y_tr)
y_p = model.predict(X_ts)</pre>
  <figure id="U9Sk" class="m_original">
    <img src="https://img1.teletype.in/files/cb/a3/cba3f6ce-fd14-4802-960e-65beccbe5a16.png" />
  </figure>
  <p id="ixzD">Аналитический прием, о котором шла речь выше, заключается в визуализации распределения разности между целями и прогнозами (ошибками). Это можно сделать, например, путем построения гистограммы или графика разброса точек с координатами по оси y - ошибки, x - предсказания. Объявим функцию с соответствующими свойствами и вызовем ее:</p>
  <pre id="aYMF" data-lang="python">def plot_residuals(target, predictions, bins_num, figsize=(20, 8), style=&#x27;seaborn&#x27;):

    error = target - predictions
    with plt.style.context(style=style):

      plt.figure(figsize=figsize)
      plt.suptitle(f&#x27;Анализ ошибок&#x27;, fontsize=16)

      plt.subplot(1, 2, 1)
      plt.hist(error, edgecolor=&#x27;blue&#x27;, bins=bins_num)
      plt.axvline(x=0, color=&#x27;black&#x27;, label=&#x27;ноль&#x27;, linestyle=&#x27;--&#x27;)
      plt.axvline(x=error.median(), color=&#x27;red&#x27;, label=&#x27;медиана&#x27;)
      plt.axvline(x=error.mean(), color=&#x27;orange&#x27;, label=&#x27;среднее&#x27;)
      plt.title(f&#x27;Гистограмма ошибок&#x27;, fontsize=15)
      plt.ylabel(&#x27;плотность распределения&#x27;, fontsize=14)
      plt.xlabel(&#x27;ошибки&#x27;, fontsize=14)
      plt.legend()

      plt.subplot(1, 2, 2)
      plt.scatter(predictions, error, alpha=0.4)
      plt.axhline(y=0, color=&#x27;red&#x27;, label=&#x27;ноль&#x27;, linestyle=&#x27;--&#x27;)
      plt.title(f&#x27;Анализ дисперсии ошибок&#x27;, fontsize=15)
      plt.ylabel(&#x27;ошибки&#x27;, fontsize=14)
      plt.xlabel(&#x27;предсказания модели&#x27;, fontsize=14)
</pre>
  <figure id="hfvE" class="m_column">
    <img src="https://img3.teletype.in/files/2d/2d/2d2d88bd-c9f9-40e2-b594-6476fd5eeae7.png" width="751" />
  </figure>
  <pre id="hDil" data-lang="python">plot_residuals(y_ts, y_p, bins_num = 10, figsize=(20, 5), style=&#x27;bmh&#x27;)</pre>
  <figure id="vNRS" class="m_column">
    <img src="https://img2.teletype.in/files/d9/04/d9041d78-763e-43ec-b143-16cb8e1a57c1.png" width="1007" />
  </figure>
  <p id="sD7k">На графике ошибки распределены равномерно относительно нуля, их среднее и медиана почти совпадают и равны 0.</p>
  <p id="tVsj">Аналогичные графики можно построить с библиотекой sklearn (потребуется использовать метод <strong>from_predictions </strong>класса <strong>PredictionErrorDisplay </strong>из модуля <strong>sklearn.metrics</strong>):</p>
  <pre id="VLWw" data-lang="python">from sklearn.metrics import PredictionErrorDisplay

PredictionErrorDisplay.from_predictions(y_ts, y_p)</pre>
  <figure id="34ox" class="m_column">
    <img src="https://img4.teletype.in/files/f8/63/f8630018-1818-4ad9-b50b-a91e519d0286.png" width="791" />
  </figure>
  <p id="Q7BB">По оси y можно вывести вместо ошибок реальные значения (цели) против предсказанных по оси x:</p>
  <pre id="DPkm" data-lang="python">PredictionErrorDisplay.from_predictions(y_ts, y_p, kind=&#x27;actual_vs_predicted&#x27;)</pre>
  <figure id="Qlva" class="m_column">
    <img src="https://img4.teletype.in/files/b2/b0/b2b09888-313c-4c9d-80a2-1da2189ef689.png" width="777" />
  </figure>
  <p id="PxVd">А теперь для демонстрационных целей добавим выброс в виде новой точки с очень большой целью и снова обучим модель:</p>
  <pre id="CvRm" data-lang="python">model.fit(pd.concat([X_tr, X_tr.iloc[[-1]]]),
          pd.concat([y_tr.to_frame(), pd.Series([1e10]).to_frame(&#x27;target&#x27;)])[&#x27;target&#x27;])

y_p = model.predict(X_ts)

plot_residuals(y_ts, y_p, bins_num = 10, figsize=(20, 5), style=&#x27;bmh&#x27;)</pre>
  <figure id="UbWD" class="m_column">
    <img src="https://img4.teletype.in/files/73/b2/73b2ec5c-725e-480f-a57f-b1653599262a.png" width="1008" />
  </figure>
  <p id="Frgj">После обучения на большом выбросе, модель для одних точек стала сильно занижать предсказания, а для других - завышать. Такой эффект может быть вызван не только выбросами в обучающих данных, но и простотой модели, плохими признаками. Например, вы пытаетесь предсказать цену квартиры, основываясь только на площади, и не учитываете особенности района, близость к метро. Тогда, если в датасете имеется перекос в сторону квартир из элитных райнов, фактор площади может остаться недооцененным.</p>
  <p id="MC0H">Таким образом, неравномерность распределения ошибок относительно нуля является индикатором того, что модель требует доработки и оптимизации, а причины могут от случая к случаю меняться.</p>
  <tt-tags id="6Wg4">
    <tt-tag name="ml">#ml</tt-tag>
    <tt-tag name="diag">#diag</tt-tag>
    <tt-tag name="ошибки">#ошибки</tt-tag>
    <tt-tag name="остатки">#остатки</tt-tag>
    <tt-tag name="residuals">#residuals</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dt_analytic/fN7IzGbL2yN</guid><link>https://teletype.in/@dt_analytic/fN7IzGbL2yN?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic</link><comments>https://teletype.in/@dt_analytic/fN7IzGbL2yN?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dt_analytic#comments</comments><dc:creator>dt_analytic</dc:creator><title>Особенности работы с LLM нейросетями в части исправления ошибок в ответах</title><pubDate>Sat, 04 May 2024 11:56:47 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/23/01/230148ee-4271-4fd8-9b58-4eab8ce602df.png"></media:content><category>машинное обучение</category><tt:hashtag>llm</tt:hashtag><tt:hashtag>chatgpt</tt:hashtag><tt:hashtag>gigachat</tt:hashtag><tt:hashtag>алиса</tt:hashtag><tt:hashtag>gemini</tt:hashtag><tt:hashtag>promts</tt:hashtag><tt:hashtag>промты</tt:hashtag><tt:hashtag>нейросети</tt:hashtag><tt:hashtag>чатботы</tt:hashtag><description><![CDATA[<img src="https://img1.teletype.in/files/cc/1d/cc1d93cb-6569-47c1-93a1-6fcfec93ff70.png"></img>После релиза ChatGPT сверхпопулярным направлением стало создание промтов. Появилось много &quot;экспертов&quot;, каждый из которых пытается предложить рецепт подходящего запроса. Пройдусь по одному из трендов - это расхожие фразы, которые призваны устранить логические ошибки .]]></description><content:encoded><![CDATA[
  <figure id="w9C4" class="m_column">
    <img src="https://img1.teletype.in/files/cc/1d/cc1d93cb-6569-47c1-93a1-6fcfec93ff70.png" width="775" />
  </figure>
  <p id="0Fu6">После релиза <strong>ChatGPT </strong>сверхпопулярным направлением стало создание промтов. Появилось много &quot;экспертов&quot;, каждый из которых пытается предложить рецепт подходящего запроса. Пройдусь по одному из трендов - это расхожие фразы, которые призваны устранить логические и фактологические ошибки в ответах.</p>
  <p id="yBJn">Спросим у 4 нейросетей <strong>ChatGPT</strong>, <strong>GigaChat</strong>, <strong>Алиса Про</strong>,<strong>Gemini </strong>одно и то же, а потом попробуем скорректировать ошибки. Запрос такой:</p>
  <blockquote id="0T90">можно ли методом compare в pandas сравнивать датафреймы с разным количеством строк. Приведи пример кода. </blockquote>
  <p id="GliL"></p>
  <p id="yxE9">Ответ <strong>Алиса Про</strong>:</p>
  <figure id="JnTN" class="m_column">
    <img src="https://img4.teletype.in/files/b1/4c/b14c4f92-081f-444e-ab5b-3a90f1da83b1.png" width="916" />
  </figure>
  <p id="s7JY">Правильный. Кстати 10 минутами до, спрашивал то же и нейросеть отвечала постоянно неправильно, приводя неработающие куски кода (может мои многократные повторы одного вопроса расценились, как неудовлетворительный результат).</p>
  <p id="uiOQ">Ответ <strong>GigaChat</strong>:</p>
  <figure id="vB4t" class="m_column">
    <img src="https://img3.teletype.in/files/e1/72/e17288e6-a597-4e4f-a7e9-4c80f8ba2769.png" width="874" />
  </figure>
  <p id="5dLR">Опять ответ верный и противоположный прежнему. Однако я вспомнил, что до этого задавал тот же запрос без &quot;в&quot; между словами compare и pandas. Вот, какой ответ в этом случае:</p>
  <figure id="NBwJ" class="m_column">
    <img src="https://img1.teletype.in/files/42/2c/422cc891-33b0-493a-a412-541c0178e060.png" width="801" />
  </figure>
  <p id="snY8">Уже ответ неверный, однако <strong>GigaChat </strong>дает нам пример другого полезного метода<strong>.</strong></p>
  <p id="Di6W">Ответ от <strong>ChatGPT</strong>:</p>
  <figure id="qhn3" class="m_column">
    <img src="https://img1.teletype.in/files/c6/6b/c66ba4f3-7e8c-4f97-b833-24cd887b4fd2.png" width="934" />
  </figure>
  <p id="UtZP">На удивление у <strong>ChatGPT </strong>ответ самый худший. Неправильный с неработающим куском кода.</p>
  <p id="V7IP">Ответ от <strong>Gemini</strong>:</p>
  <figure id="Rbrt" class="m_column">
    <img src="https://img2.teletype.in/files/50/df/50dfa916-849c-4b7b-8306-3183bcbe1c93.png" width="778" />
  </figure>
  <p id="933i">Неправильный с неработающим кодом. При этом удивительно, что 10 минут назад выдавала стабильно правильный ответ. Единственный момент - я запустил этот и предыдущие вопросы в рамках одного чата, соответственно, на ответ повлиял контекст. Действительно, открыв новый чат, снова получил правильный ответ. </p>
  <p id="2BGZ">Резюме:</p>
  <p id="CKy7">- 3 нейросети (кроме <strong>ChatGPT</strong>) результат поменяли на противоположный в течение нескольких минут.</p>
  <p id="ngzR">- На результат могут повлиять даже незначительные словесные элементы (в нашем примере - частицы)</p>
  <p id="H9UQ">- На результат сильно влияет контекст</p>
  <p id="WPpJ">А теперь проверим, &quot;чувствительность&quot; к промту по исправлению ошибок <strong>Gemini </strong>и <strong>ChatGPT</strong>.</p>
  <blockquote id="NuVP">можно ли методом compare в pandas сравнивать датафреймы с разным количеством строк. Приведи пример кода. <strong>Убедись, что пример работает и в нем нет ошибок.</strong></blockquote>
  <p id="pzM8"><strong>ChatGPT</strong>:</p>
  <figure id="Ni2N" class="m_column">
    <img src="https://img1.teletype.in/files/43/a7/43a77357-04a0-4ff2-b561-c57b9e5a7b48.png" width="980" />
  </figure>
  <p id="8T8T">Опять пример неработающий.</p>
  <p id="t1ik"><strong>Gemini</strong>:</p>
  <figure id="KB4D" class="m_column">
    <img src="https://img4.teletype.in/files/bf/8f/bf8f6741-1ed8-4305-9b6d-8d9564acfd32.png" width="822" />
    <figcaption>...</figcaption>
  </figure>
  <figure id="r12v" class="m_column">
    <img src="https://img2.teletype.in/files/50/e7/50e75d14-e8af-494e-8724-c854442de40d.png" width="814" />
  </figure>
  <p id="uXm2"></p>
  <p id="7Dk1"><strong>Gemini </strong>в данном случае дала опять же неправильный ответ, но уже работающий код, возможно, призыв протестировать на нее частично повлиял.</p>
  <p id="gBfZ">Отмечу, что проверял и другие промты, нацеленные на исправление ошибок:</p>
  <p id="JHrn">&quot;<em>запрос</em>...<strong>Если есть в коде ошибки, найди и устрани их</strong>&quot;</p>
  <p id="T6eL">&quot;<em>запрос</em>...<strong>Проверь код в интерпретаторе Python и исправь ошибки</strong>&quot;</p>
  <p id="q4fE">В целом такими промтами повлиять на результат <strong>ChatGPT</strong>, а также<strong> GigaChat</strong>, <strong>Алиса Про </strong>(когда они давали неверный результат) не удалось. Только с <strong>Gemini</strong> частично получилось.</p>
  <p id="LCiY"></p>
  <p id="bhY5">Вот еще пример. Зададим вопрос: </p>
  <blockquote id="h9ac">когда губернатор штата Калифорния Гэвин Ньюсом подписал закон, который запрещал использование частных тюрем для содержания нелегальных иммигрантов.</blockquote>
  <p id="Y63h">Из СМИ известно, что в октябре 2019 года.</p>
  <p id="YmRj"><strong>Алиса Про</strong>:</p>
  <figure id="qWee" class="m_column">
    <img src="https://img3.teletype.in/files/e8/b3/e8b336b6-ae4b-4195-b058-87e559fe4750.png" width="923" />
  </figure>
  <p id="tNpd"><strong>GigaChat</strong>:</p>
  <figure id="TROt" class="m_column">
    <img src="https://img2.teletype.in/files/13/08/13087a36-4da9-4644-aa7b-3ff048724019.png" width="797" />
  </figure>
  <p id="NcxM">Сам я точную дату не знаю, но в октябре 2019. То есть с месяцем <strong>GigaChat </strong>не ошибся. Опять же пару часов назад, он отказывался отвечать на этот вопрос. То есть результаты не стабильны.</p>
  <p id="4PXP"><strong>ChatGPT</strong>:</p>
  <figure id="wQu1" class="m_column">
    <img src="https://img4.teletype.in/files/34/ae/34aef627-54cd-481f-a24a-f6c870f71ba7.png" width="949" />
  </figure>
  <p id="BkcL"><strong>Gemini</strong>:</p>
  <figure id="n8L2" class="m_column">
    <img src="https://img2.teletype.in/files/9c/d8/9cd8fc4b-3df0-4624-ba8c-b48653ffd76f.png" width="830" />
  </figure>
  <p id="ynNi"></p>
  <p id="lveg"></p>
  <p id="ZsjI">Получается <strong>ChatGPT </strong>ошибся. Попробуем улучшающий промт:</p>
  <blockquote id="6pPL">когда губернатор штата Калифорния Гэвин Ньюсом подписал закон, который запрещал использование частных тюрем для содержания нелегальных иммигрантов. Проверь, чтобы все даты и факты были правильными</blockquote>
  <p id="oOCp"></p>
  <figure id="wBTL" class="m_column">
    <img src="https://img3.teletype.in/files/61/65/6165125e-64fb-4aa5-852a-3dff708639d3.png" width="809" />
  </figure>
  <p id="Qex6">Интересно, но в данном случае он исправил месяц. При этом более 5 раз до выдавал дату 11 сентября.</p>
  <p id="I52a">Уже после спрашиваю в новом чате в первоначальной формулировке (без дополнения промта просьбой перепроверить факты и даты) <strong>ChatGPT </strong>отвечает - 25 октября. То есть опять же результаты не стабильны, возможно, коррекция ответа вызвана настойчивыми попытками задать один и тот же вопрос, а возможно эффектом от просьбы перепроверить.</p>
  <p id="zUe1">Таким образом, нельзя говорить о стабильном влиянии на результат &quot;перероверяющих&quot; промтов (&quot;проверь факты и даты&quot;, &quot;если есть ошибки, найди и устрани их&quot;). Возможно, в отдельных случаях они влияют, но в большинстве - нет.</p>
  <p id="A9T6">Моя рекомендация: </p>
  <p id="bPvr">- спрашивать у разных нейросетей, выбирая подходящий ответ</p>
  <p id="X6ct">- для разных тематик открывать отдельные чаты, чтобы минимизировать влияние контекста. </p>
  <tt-tags id="PgNs">
    <tt-tag name="llm">#llm</tt-tag>
    <tt-tag name="chatgpt">#chatgpt</tt-tag>
    <tt-tag name="gigachat">#gigachat</tt-tag>
    <tt-tag name="алиса">#алиса</tt-tag>
    <tt-tag name="gemini">#gemini</tt-tag>
    <tt-tag name="promts">#promts</tt-tag>
    <tt-tag name="промты">#промты</tt-tag>
    <tt-tag name="нейросети">#нейросети</tt-tag>
    <tt-tag name="чатботы">#чатботы</tt-tag>
  </tt-tags>

]]></content:encoded></item></channel></rss>