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