<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Блог Артёма Агасиева: заметки с тегом Python</title>
<link>https://agasiev.com/tags/python/</link>
<description>Telegram: @aagasiev</description>
<author>Артём Агасиев</author>
<language>ru</language>
<generator>E2 (v3576; Aegea)</generator>

<itunes:owner>
<itunes:name>Артём Агасиев</itunes:name>
<itunes:email></itunes:email>
</itunes:owner>
<itunes:subtitle>Telegram: @aagasiev</itunes:subtitle>
<itunes:image href="" />
<itunes:explicit></itunes:explicit>

<item>
<title>Определяем расширение файла в Python</title>
<guid isPermaLink="false">118</guid>
<link>https://agasiev.com/all/opredelyaem-rashireniye-fayla-v-python/</link>
<pubDate>Sat, 29 Jul 2023 03:57:17 +0300</pubDate>
<author>Артём Агасиев</author>
<comments>https://agasiev.com/all/opredelyaem-rashireniye-fayla-v-python/</comments>
<description>
&lt;p&gt;Потребовалось сейчас сделать быструю проверку того, что тип скачиваемого программой файла — картинка в формате webp, а если это не она, то не качать ее вовсе, ну или хотя бы не полностью.&lt;/p&gt;
&lt;p&gt;Самый быстрый способ — сделать HEAD запрос и проверить content-type, например, с помощью питоновской библиотеки mimetypes:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python3"&gt;
import requests
import mimetypes

r = requests.head("https://.../image.webp")
file_ext = mimetypes.guess_extension(r.headers.get("content-type"), strict=False)

if file_ext == "webp":
    pass
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Можно ли точно полагаться на ответ сервера? В целом, нет, ведь даже если этот заголовок с виду корректен, то в тело ответа сервер может запихнуть что угодно. Однако, можно отсеивать очевидно ненужные варианты типа text/plain или чего-то подобного.&lt;/p&gt;
&lt;p&gt;А еще бывает так, что нужный файл отдается с не тем типом контента. Например, в моем случае нужное хранилище AWS S3 грешило тем, что отдавало файл картинки с заголовком binary/octet-stream.&lt;/p&gt;
&lt;p&gt;А еще HEAD запрос может не поддерживаться веб-сервером.&lt;/p&gt;
&lt;p&gt;И что делать-то? Будем использовать обертку над libmagic, библиотеку &lt;a href="https://pypi.org/project/python-magic/"&gt;python-magic&lt;/a&gt;. Она позволит нам по небольшому чанку данных узнать mime тип получаемого контента.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python3"&gt;
import os
import magic
import requests
import mimetypes

local_filename = "/local/file/name.webp"

r = requests.get("https://.../image.webp", stream=True)
f = open(local_filename, "wb")

for chunk in r.iter_content(1024):
    mime = magic.from_buffer(chunk, mime=True)
    file_ext = mimetypes.guess_extension(mime, strict=False)

    if new_file_ext != "webp":
        f.close()
        os.remove(local_filename)
        break

    f.write(chunk)
else:
    f.close()
    # Тут уже что-то можем делать с полученным файлом.

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Вроде, выглядит норм. Ну, еще обработку ошибок добавить бы не помешало, да.&lt;/p&gt;
&lt;p&gt;Но вот с точки зрения работы с сетью есть некоторые сомнения, что в случае вызова break, requests реально считает только 1 кб данных. Беглое гугление скорее усугубило мои сомнения. Надо иметь это в виду и как-нибудь на досуге реально протестировать.&lt;/p&gt;
&lt;p&gt;В общем, все.&lt;/p&gt;
&lt;p&gt;P.S. Если mimetypes не смогла определить расширение файла webp по типу контента image/webp, то его стоит добавить вручную в один из файлов mime.types вашей системы. Например, для Ubuntu это файл /etc/mime.types, в который надо добавить строку:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="shell"&gt;
image/webp    webp
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Другие файлики, откуда mimetypes может загружать себе данные, прописаны в самой библиотеке в массиве knownfiles:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python3"&gt;
knownfiles = [
    "/etc/mime.types",
    "/etc/httpd/mime.types",                    # Mac OS X
    "/etc/httpd/conf/mime.types",               # Apache
    "/etc/apache/mime.types",                   # Apache 1
    "/etc/apache2/mime.types",                  # Apache 2
    "/usr/local/etc/httpd/conf/mime.types",
    "/usr/local/lib/netscape/mime.types",
    "/usr/local/etc/httpd/conf/mime.types",     # Apache 1.2
    "/usr/local/etc/mime.types",                # Apache 1.3
]
&lt;/code&gt;
&lt;/pre&gt;
</description>
</item>

<item>
<title>Определение языка текста на Python</title>
<guid isPermaLink="false">82</guid>
<link>https://agasiev.com/all/opredelenie-yazyka-teksta-na-python/</link>
<pubDate>Fri, 07 May 2021 17:21:10 +0300</pubDate>
<author>Артём Агасиев</author>
<comments>https://agasiev.com/all/opredelenie-yazyka-teksta-na-python/</comments>
<description>
&lt;p&gt;Для задач NLP бывает полезно предварительно определить язык текста с которым мы сейчас работаем.&lt;/p&gt;
&lt;p&gt;Например, это может пригодиться в случае, если:&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Какая-то наша модель умеет работать корректно только с определенным набором языков&lt;/li&gt;
&lt;li&gt;Для каждого языка у нас есть отдельная модель&lt;/li&gt;
&lt;li&gt;Текст на разных языках нужно по разному подготавливать: выбрать нужный стеммер или токенайзер — особенно важно для китайского и японского языков.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;В работе я использую для решения этой задачи три библиотеки: &lt;a href="https://github.com/facebookresearch/fastText/"&gt;fastText&lt;/a&gt; от Facebook, &lt;a href="https://github.com/google/cld3"&gt;Compact Language Detector v3&lt;/a&gt; от Google и &lt;a href="https://github.com/Mimino666/langdetect"&gt;langdetect&lt;/a&gt;. У каждой из них свои преимущества и недостатки связанные с размерами моделей, скоростью работы и точностью. Но, в целом, судя по опыту, точнее всего работает именно fastText.&lt;/p&gt;
&lt;p&gt;Для &lt;a href="https://fasttext.cc/docs/en/language-identification.html"&gt;задачи определения языка&lt;/a&gt; у fastText есть две готовые модели: &lt;a href="https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin"&gt;побольше, на 126 мб&lt;/a&gt; и &lt;a href="https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.ftz"&gt;поменьше, на 917 кб&lt;/a&gt;. Вторая будет менее точная, но обе поддерживают одинаковое количество языков — 176 штук.&lt;/p&gt;
&lt;p&gt;Качаем обе и посмотрим как с ними работать:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="shell"&gt;
wget https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin
wget https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.ftz
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Загружаем обе модели:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python3"&gt;
import fastText

model_big = fastText.load_model('./lid.176.bin')
model_small = fastText.load_model('./lid.176.ftz')
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Пробуем в работе:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python3"&gt;
print(model.predict(["hi"]))
print(model_small.predict(["hi"]))
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;И получаем довольно странный результат:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python3"&gt;
([['__label__ca']], [array([0.5109927], dtype=float32)])
([['__label__en']], [array([0.12450418], dtype=float32)])
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Почему так? Библиотека настроена на работу с предложениями, а не с отдельными словами, поэтому точность на очень коротких текстах будет хромать. Хотя, забавно, что маленькая модель сработала тут лучше, чем большая. Попробуем с текстом подлиннее:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python3"&gt;
print(model.predict(["hi there, human"]))
print(model_small.predict(["hi there, human"]))
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;И получаем вполне приемлемый результат:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python3"&gt;
([['__label__en']], [array([0.84252757], dtype=float32)])
([['__label__en']], [array([0.83792776], dtype=float32)])
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Когда использовать какую модель из двух? Это зависит от желаемой точности и скорости работы. Если важнее точность, то можно использовать большую модель, а если скорость, то маленькую. Главное, если мы применяем определение языка в пайплайне обучения, например, классификатора спама, использовать, по возможности, ту же самую модель и в продакшне. А то итоговое качество может сильно хромать.&lt;/p&gt;
</description>
</item>

<item>
<title>Ошибка при работе с библиотекой implicit на GPU</title>
<guid isPermaLink="false">80</guid>
<link>https://agasiev.com/all/oshibka-pri-rabote-s-bibliotekoy-implicit-na-gpu/</link>
<pubDate>Mon, 03 May 2021 01:50:50 +0300</pubDate>
<author>Артём Агасиев</author>
<comments>https://agasiev.com/all/oshibka-pri-rabote-s-bibliotekoy-implicit-na-gpu/</comments>
<description>
&lt;p&gt;Работаю тут с библиотекой implicit для питона, настроил всю инфраструктуру CUDA, прописал все пути, но все равно ловил странное исключение, при попытке использования ALS на GPU:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;No CUDA extension has been built, can't train on GPU&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Чтож, идем читать исходники &lt;a href="https://github.com/benfred/implicit/blob/master/implicit/gpu/als.py"&gt;als.py&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python"&gt;
if not implicit.gpu.HAS_CUDA:
    raise ValueError("No CUDA extension has been built, can't train on GPU.")
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Очевидно, HAS_CUDA == False, но с чего это вдруг? Находим инициализацию этой переменной в &lt;a href="https://github.com/benfred/implicit/blob/master/implicit/gpu/__init__.py"&gt;__init__.py&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python"&gt;
try:
    import cupy  # noqa

    from ._cuda import *  # noqa

    HAS_CUDA = True
except ImportError:
    HAS_CUDA = False
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Файл _cuda — локальный, значит остается только проверить наличие &lt;a href="https://docs.cupy.dev"&gt;cupy&lt;/a&gt; на рабочей машине. И правда, все дело было именно в её отсутствии. Ну хоть сообщить то об этом нормально можно было? :(&lt;/p&gt;
&lt;p&gt;Устанавливаем по  &lt;a href="https://docs.cupy.dev/en/stable/install.html"&gt;инструкции&lt;/a&gt;, запускаем fit на ALS и вуаля, все работает. На RTX 3090 прирост скорости обучения, по сравнению с Xeon W-2265, у меня был примерно в 20 раз, на разреженной матрице размерности 300000х100000.&lt;/p&gt;
</description>
</item>

<item>
<title>Ошибка при сборке Python биндинга для библиотеки Facebook StarSpace</title>
<guid isPermaLink="false">78</guid>
<link>https://agasiev.com/all/oshibka-pri-sborke-python-bindinga-dlya-biblioteki-facebook-star/</link>
<pubDate>Tue, 15 Dec 2020 16:45:32 +0300</pubDate>
<author>Артём Агасиев</author>
<comments>https://agasiev.com/all/oshibka-pri-sborke-python-bindinga-dlya-biblioteki-facebook-star/</comments>
<description>
&lt;p&gt;При сборке Python биндинга крутой библиотеки &lt;a href="https://github.com/facebookresearch/StarSpace"&gt;StarSpace&lt;/a&gt; получил такую ошибку:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python"&gt;
Traceback (most recent call last):
  File "test.py", line 1, in &lt;module&gt;
    import starwrap as sw
ImportError: /www/home/mntlp/Starspace/python/test/starwrap.so: undefined symbol: _Py_ZeroStruct
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Возникает она потому, что я в данный момент работаю под anaconda, а биндинг собрался под другую версию Python. Пофиксить несложно. Открываем CMakeLists.txt и в начала файла, после строки&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="shell"&gt;
project(starspace)
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;устанавливаем пути к библиотекам и инклудам нужного нам питона:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="shell"&gt;
set(PYTHON_LIBRARY "/home/user/anaconda3/lib")
set(PYTHON_INCLUDE_DIR "/home/user/anaconda3/include/python3.6m/")
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;А если работаем под виртуальной средой в anaconda, то вместо предыдущих путей нужно указать путь через нее:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="shell"&gt;
set(PYTHON_LIBRARY "/home/user/anaconda3/envs/env_name/lib")
set(PYTHON_INCLUDE_DIR "/home/user/anaconda3/envs/env_name/include/python3.6m")
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Дальше:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="shell"&gt;
cd ./build
cmake --build .
cd -
cp ./build/starwrap.so ./test
cd test
python3 test.py
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;В общем, все как было в build.sh до возникновения ошибки.&lt;/p&gt;
</description>
</item>

<item>
<title>Обрабатываем некорректно сохраненные в лог JSON данные</title>
<guid isPermaLink="false">74</guid>
<link>https://agasiev.com/all/obrabatyvaem-nekorrektno-sohranennye-v-log-json-dannye/</link>
<pubDate>Thu, 10 Dec 2020 18:15:29 +0300</pubDate>
<author>Артём Агасиев</author>
<comments>https://agasiev.com/all/obrabatyvaem-nekorrektno-sohranennye-v-log-json-dannye/</comments>
<description>
&lt;p&gt;С месяц назад, при записи JSON данных в лог, забыл отформатировать их json.dumps из питоновского dict’a в нормальный вид, а просто записал его при форматной печатью, преобразовав dict в str. Само собой, получил кучу данных в неудобоваримом виде,  эх.&lt;/p&gt;
&lt;p&gt;Как исправить? Стандартный json.loads теперь не воспримет такую строку как корректный JSON, т. к. с точки зрения формата она не валидна. Можно решить эту проблему при помощи функции &lt;a href="https://docs.python.org/3/library/ast.html#ast.literal_eval"&gt;ast.literal_eval&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Она безопасно преобразует строку, содержащую символы или стандартные структуры питона в нужный элемент или объект. В нашем случае, строковое представление словаря в сам словарь. А значит, так можно пробежаться по всем логам и преобразовать их в корректный вид.&lt;/p&gt;
&lt;p&gt;Пример работы:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="python"&gt;
import ast
import json

# Наш преобразованный в строку dict()
s = "{'a': 'Text with \\'quotes\\''}"
j = json.loads(s)
# Получаем ошибку парсинга из-за одинарных кавычек
&gt;&gt; json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

# Парсим строку в dict
j = ast.literal_eval(s)
print(j)
&gt;&gt; {'a': "Text with 'quotes'"}
# Теперь уже дампим данные корректно
print(json.dumps(j))
&gt;&gt;{"a": "Text with 'quotes'"}
&lt;/code&gt;
&lt;/pre&gt;
</description>
</item>

<item>
<title>Шпаргалка по формам/алгоритмам нормализации Unicode текста</title>
<guid isPermaLink="false">70</guid>
<link>https://agasiev.com/all/unicode-normal-forms-python/</link>
<pubDate>Sun, 06 Sep 2020 17:42:26 +0300</pubDate>
<author>Артём Агасиев</author>
<comments>https://agasiev.com/all/unicode-normal-forms-python/</comments>
<description>
&lt;p&gt;Алгоритмы нормализации в Unicode нужны для преобразования внутренней структуры текста, чтобы потом с ним было проще работать. Например, можно заменить &lt;a href="https://ru.wikipedia.org/wiki/Комбинируемый_символ"&gt;несколько символов одним&lt;/a&gt;, убрать все диакритические знаки из текста и даже преобразовать похожие буквы в их аналоги.&lt;/p&gt;
&lt;p&gt;Всего есть четыре таких алгоритма: NFD, NFC, NFKD, NFKC. Каждый в отдельности можно запускать на одной и той же строке много раз, и результат от этого никак не изменится. То есть они &lt;a href="https://ru.wikipedia.org/wiki/Идемпотентность"&gt;идемпотентны&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Работать с Unicode текстом будем при помощи страндартной Python библиотеки &lt;a href="https://docs.python.org/3/library/unicodedata.html"&gt;unicodedata&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;1. NFD (Normalization Form Canonical Decomposition) или форма нормализации D.&lt;/h2&gt;
&lt;p&gt;Раскладывает составные символы на несколько простых в соответствии с таблицами декомпозиции. Если хотя бы один из получившихся символов тоже составной, раскладываем и его до тех пор, пока не получим последовательность простых символов. То есть, алгоритм работает рекурсивно.&lt;/p&gt;
&lt;p&gt;Получившаяся разложенная последовательность сортируется в некотором порядке, пока не очень понял в каком именно.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;for c in unicodedata.normalize(&amp;quot;NFD&amp;quot;, &amp;quot;ё&amp;quot;):
    print(&amp;quot;'%s': %s&amp;quot; % (c, unicodedata.name(c)))

&amp;gt;&amp;gt; 'е': CYRILLIC SMALL LETTER IE
&amp;gt;&amp;gt; '̈': COMBINING DIAERESIS&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Так можно быстро подчистить всю диакритику в тексте, удаляя все ненужные группы символов из строки и оставляя только буквы.&lt;/p&gt;
&lt;h2&gt;2. NFC (Normalization Form Canonical Composition) или форма нормализации C.&lt;/h2&gt;
&lt;p&gt;Сначала выполняет NFD декомпозицию, а затем комбинирует полученные простые символы в составные. NFD декомпозиция тут нужна, чтобы разбить уже частично комбинированные символы на простые составляющие для последующей сортировки и обратной сборки.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;for c in unicodedata.normalize(&amp;quot;NFC&amp;quot;, &amp;quot;ё&amp;quot;):
    print(&amp;quot;'%s': %s&amp;quot; % (c, unicodedata.name(c)))

&amp;gt;&amp;gt; 'ё': CYRILLIC SMALL LETTER IO&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Так можно быстро «приклеить» всю диакритику к буквам и получить из двух символов CYRILLIC SMALL LETTER IE и COMBINING DIAERESIS один CYRILLIC SMALL LETTER IO.&lt;/p&gt;
&lt;h2&gt;3. NFKD (Normalization Form Compatibility Decomposition) или форма нормализации KD.&lt;/h2&gt;
&lt;p&gt;Алгоритм, который выполняет NFD декомпозицию и заменяет похожие символы совместимыми аналогами, например, дробь ’¼’ заменяется на строку символов «1/4».&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;s = '⑲ ⁹ ¼'
print(unicodedata.normalize(&amp;quot;NFKD&amp;quot;, s))

&amp;gt;&amp;gt; 1 19 9 1/4

for c in list(s):
    print(&amp;quot;'%s': %s - '%s'&amp;quot; % (c, unicodedata.name(c), unicodedata.normalize(&amp;quot;NFKD&amp;quot;, c)))

&amp;gt;&amp;gt; '⑲': CIRCLED NUMBER NINETEEN - '19'
&amp;gt;&amp;gt; ' ': SPACE - ' '
&amp;gt;&amp;gt; '⁹': SUPERSCRIPT NINE - '9'
&amp;gt;&amp;gt; ' ': SPACE - ' '
&amp;gt;&amp;gt; '¼': VULGAR FRACTION ONE QUARTER - '1⁄4'&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;После такой нормализации можно легко делать фильтрацию текста регэкспами, если его пытались усложнить и замаскировать от этого заменой похожих символов.&lt;/p&gt;
&lt;h2&gt;4. NFKC (Normalization Form Compatibility Composition) или форма нормализации KC.&lt;/h2&gt;
&lt;p&gt;Сначала выполняется совместимое разложение NFKD, а затем символы собираются вместе согласно NFC. Аналогично работе с NFC, можно быстро склеить всю диакритику с буквами, приведенными к некоторому базовому виду.&lt;/p&gt;
&lt;h2&gt;Проблемы NFKD и NFKC&lt;/h2&gt;
&lt;p&gt;При всем удобстве алгоритмов NFKD и NFKC, они не приводят некоторую часть визуально похожих символов к совместимым аналогам. Например, группу &lt;b&gt;Negative Circled Number &lt;/b&gt;* из блока &lt;a href="https://unicode-table.com/en/blocks/enclosed-alphanumerics/"&gt;Enclosed Alphanumerics&lt;/a&gt; вполне можно привести к числам, но этого не происходит:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;unicodedata.normalize(&amp;quot;NFKC&amp;quot;, &amp;quot;⓫ ⓯&amp;quot;)

&amp;gt;&amp;gt; '⓫ ⓯'&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;А значит, если хочется сделать качественное приведение похожих символов, необходимое для многих задач, придется повозиться над своими таблицами замен.&lt;/p&gt;
</description>
</item>

<item>
<title>fastText эмбеддинги</title>
<guid isPermaLink="false">53</guid>
<link>https://agasiev.com/all/facebook-fasttext/</link>
<pubDate>Mon, 23 Sep 2019 14:56:38 +0300</pubDate>
<author>Артём Агасиев</author>
<comments>https://agasiev.com/all/facebook-fasttext/</comments>
<description>
&lt;p&gt;&lt;a href="https://fasttext.cc"&gt;fastText&lt;/a&gt; — библиотека для векторного представления слов и классификации текстов от Facebook AI Research.&lt;/p&gt;
&lt;p&gt;Для её сборки, нам потребуется компилятор с поддержкой C++11 (gcc-4.6.3, clang-3.3 и новее):&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;git clone https://github.com/facebookresearch/fastText.git
cd fastText
sudo python3 -m pip install .&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Если у вас установлено несколько компиляторов разных версий, то можно запустить установку, явно указав путь к нужной версии компилятора:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;sudo CC='/usr/bin/gcc-4.9' python3 -m pip install .&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Используем уже натренированную модель&lt;/h2&gt;
&lt;p&gt;Тренированную модель для русского языка можно скачать &lt;a href="https://github.com/facebookresearch/fastText/blob/master/docs/crawl-vectors.md"&gt;тут&lt;/a&gt;. В архиве занимает 4.2 Гб, распакованная 6.8 Гб:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;wget https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ru.300.bin.gz
gunzip cc.ru.300.bin.gz&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Использовать можно так:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;from fasttext import load_model

model = load_model(&amp;quot;cc.ru.300.bin&amp;quot;)&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Тренируем свою модель&lt;/h2&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;from fasttext import train_unsupervised

model = train_unsupervised(
    input=&amp;quot;dataset.txt&amp;quot;, 
    model='skipgram'
)

model.save_model(&amp;quot;skipgram_model.bin&amp;quot;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;На Core i7-6700 с датасетом в 20 млн строк, модель обучилась в течении 5 эпох за 35 минут и весила ~1.1 Гб.&lt;/p&gt;
&lt;h2&gt;Используем&lt;/h2&gt;
&lt;p&gt;Для оценки похожести/близости векторов будем использовать &lt;a href="https://ru.wikipedia.org/wiki/Векторная_модель"&gt;косинусное сходство&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;
$$ \mathrm{similarity} = \mathrm{cos(}\theta\mathrm{)} = \dfrac{A \cdot B}{\left\lVert A \right\rVert \cdot \left\lVert B \right\rVert}  $$
&lt;/p&gt;
&lt;p&gt;В коде будет выглядеть так:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;import numpy as np

def similarity(v1, v2):
    n1 = np.linalg.norm(v1)
    n2 = np.linalg.norm(v2)

    if n1 &amp;lt; 1e-6 or n2 &amp;lt; 1e-6:
        return 0.0
    else:
        return np.dot(v1, v2) / n1 / n2

def word_similarity(model, w1, w2):
    return similarity(model.get_word_vector(w1), model.get_word_vector(w2))

def sentence_similarity(model, t1, t2):
    return similarity(model.get_sentence_vector(t1), model.get_sentence_vector(t2))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Проверяем:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;gt;&amp;gt;&amp;gt; word_similarity(model, &amp;quot;приветик&amp;quot;, &amp;quot;привет&amp;quot;)
0.7990662

&amp;gt;&amp;gt;&amp;gt; sentence_similarity(model, &amp;quot;сколько тебе лет&amp;quot;, &amp;quot;а лет-то тебе сколько&amp;quot;)
0.85209394&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Про классификацию текста при помощи fastText напишу чуть позже.&lt;/p&gt;
</description>
</item>


</channel>
</rss>