make some code
84 subscribers
13 photos
33 links
Репосты с блога makesomecode.me.
Небольшие интересности из C#/Python/Go. Пишу редко — надоесть не успею.
Download Telegram
Периодическое выполнение задач

Внезапно возникла задача периодического выполнения функции в Python приложении (очередной бот для telegram).
Писать с нуля, конечно, можно, но довольно быстро нашлёлся проект schedule на github.

Библиотека замечательная своим простым интерфейсом, но подходит не для всех задач (со слов самого автора):
1. Работает только in-memory. Для хранения информации между перезапусками потребуется городить свой слой хранения
2. Точность не самая высокая (для high frequency trading и подобных задач не подойдёт)
3. Выполнение в многопоточном окружении надо делать самостоятельно (но это не сложно)
4. Нет локализации и часовых поясов: не учитывает временные зоны, календари рабочего времени и пр.

Пример использования (из документации):
import schedule
import time

def job():
print("I'm working...")

schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
schedule.every().minute.at(":17").do(job)

while True:
schedule.run_pending()
time.sleep(1)

Пример на colab

#python #lib
pydantic — очередная Python-библиотека, которой мне не хватало.
Если вкратце, то это библиотека для валидации при сериализации/десериализации JSON в/из Python-объекта реализованная на стандартных type hint-ах.

В библиотеке есть класс BaseModel. Класс используется для базовой функциональности валидации и содержит дополнительные полезные методы. Например, dict(), json():

from typing import List
from pydantic import BaseModel

class Foo(BaseModel):
count: int
size: float = None

class Bar(BaseModel):
apple = 'x'
banana = 'y'

class Spam(BaseModel):
foo: Foo
bars: List[Bar]

m = Spam(foo={'count': 4}, bars=[{'apple': 'x1'}, {'apple': 'x2'}])
print(m)
#> foo=Foo(count=4, size=None) bars=[Bar(apple='x1', banana='y'),
#> Bar(apple='x2', banana='y')]

print(m.dict())
"""
{
'foo': {'count': 4, 'size': None},
'bars': [
{'apple': 'x1', 'banana': 'y'},
{'apple': 'x2', 'banana': 'y'},
],
}
"""

Библиотека может использоваться совместно с ORM. Например, sqlalchemy.

pydantic содержит класс ValidationError, который позволяет получить ошибки валидации в структурированном виде. Например, в JSON:

from typing import List
from pydantic import BaseModel, ValidationError, conint


class Location(BaseModel):
lat = 0.1
lng = 10.1


class Model(BaseModel):
is_required: float
gt_int: conint(gt=42)
list_of_ints: List[int] = None
a_float: float = None
recursive_model: Location = None


data = dict(
list_of_ints=['1', 2, 'bad'],
a_float='not a float',
recursive_model={'lat': 4.2, 'lng': 'New York'},
gt_int=21,
)

try:
Model(**data)
except ValidationError as e:
print(e.json())
"""
[
{
"loc": [
"is_required"
],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": [
"gt_int"
],
"msg": "ensure this value is greater than 42",
"type": "value_error.number.not_gt",
"ctx": {
"limit_value": 42
}
},
{
"loc": [
"list_of_ints",
2
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
},
{
"loc": [
"a_float"
],
"msg": "value is not a valid float",
"type": "type_error.float"
},
{
"loc": [
"recursive_model",
"lng"
],
"msg": "value is not a valid float",
"type": "type_error.float"
}
]
"""


К сожалению, информации о возможности локализации не нашёл.

Примеры можно попробовать в google colab

#python #lib
В очередной раз после переустановки Windows осознал, что надо накатить драйвера, CUDA, cuDNN, Tensorflow/Keras для обучения нейронных сетей.

Каждый раз для меня это оказывается несложной, но времязатратной операцией: найти подходящую комбинацию Tensorflow/Keras, CUDA, cuDNN и Python несложно, но вспоминаю про эти зависимости только в тот момент, когда при импорте Tensorflow вижу, что видеокарта не обнаружена и начинаю поиск нужной страницы в документации Tensorflow.

В этот раз ситуация немного усложнилась. Помимо установки Tensorflow мне потребовалось установить PyTorch.

https://makesomecode.me/2021/07/cuda-cudnn-tf-pytorch-windows-installation/
Форматированный вывод в Jupyter Notebook

Запишу, чтобы не забыть и, вдруг, вы тоже про это не помните.

Jupyter Notebook отличная штука для прототипирования ML моделей и визуализации данных. Однако, для того, чтобы вспомнить как вывести в output форматированный текст мне приходится каждый раз искать в интернете правильный модуль.

Правильный ответ здесь — IPython.display. Модуль содержит методы для вывода разного контента: текста, аудио, изображений и пр. Например, можно вывести в output форматированный Markdown текст:

from IPython.display import display, Markdown

display(Markdown('[**_Hello_ world**](https://google.com)'))

В output получим форматированную строку "Hello world", которая будет является ссылкой на https://google.com.

Пример на google colab

#python #jupyter
Возникла идея простого бота, который будет отправлять переданные ему файлы в облако. Однако для аутентификации сейчас практически везде используется OAuth2. Если в web-приложениях пользоваться OAuth2 все уже научились, то с telegram у меня возникли вопросы. В этой заметке будет минимальный пример telegram-бота, который работает с box.com через API с аутентификацией через OAuth2. disclaimer #1: В статье не будет подробного описания работы OAuth2 — только необходимый минимум информации для понимания процесса.

Продолжение

#python
Справка и исходники в IPython

Думаю, все здесь знают про функции help и dir, которые позволяют в REPL посмотреть справку по функции или классу и получить список методов и атрибутов объекта соответственно:

>>> help(help)
Help on _Helper in module _sitebuiltins object:
...
>>> dir(list)
['__add__',
'__class__',
'__class_getitem__',
...
'remove',
'reverse',
'sort']


Но только сегодня я узнал, что в интерактивном шелле IPython можно посмотреть исходный код функции или класса при помощи команды (операции, функции?) ??:

>>> help??
Signature: help(*args, **kwds)
Type: _Helper
String form: Type help() for interactive help, or help(object) for help about object.
Namespace: Python builtin
File: c:\program files\python39\lib\_sitebuiltins.py
Source:
class _Helper(object):
"""Define the builtin 'help'.

This is a wrapper around pydoc.help that provides a helpful message
when 'help' is typed at the Python interactive prompt.

Calling help() at the Python prompt starts an interactive help session.
Calling help(thing) prints help for the python object 'thing'.
"""

def __repr__(self):
return "Type help() for interactive help, " \
"or help(object) for help about object."
def __call__(self, *args, **kwds):
import pydoc
return pydoc.help(*args, **kwds)


Так же это работает и в Jupyter Notebook/Lab или Google Colab. Пример, можно посмотреть в блокноте

#TIL #python
Всё ещё пользуетесь GridSearchCV? Тогда мы идём к вам!

В последнее время замечаю, что народ соскакивает с проверенного временем метода подбора параметров моделей при помощи GridSearchCV из модуля model_selection библиотеки scikit-learn на библиотеку optuna.

Судя по Google Trends эта волна началась около трёх лет назад, но я узнал про библиотеку лишь несколько месяцев назад и успел применить только в паре соревнований.

Написал небольшую заметку почему тоже перешёл на эту библиотеку в блоге.

#python #lib
Preview для любых текстовых файлов

По умолчанию в Windows в панели предпросмотра отображается содержимое для ограниченного числа текстовых форматов.

Но помимо .txt есть большое количество других, не менее полезных, текстовых форматов. Например, .xml, .py, .md или .log.

Исправить это недоразумение можно просто: необходимо добавить нужный ключ в реестр. Для этого есть два способа. Покажу на примере .md.

Первый способ:
1. Открыть редактор реестра: Win+R -> regedit -> Enter
2. В редакторе реестра найти ветку HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.md. Если такой нет, то создать
3. Добавить строковый атрибут с именем PerceivedType и значением text

Второй способ:
1. Открыть терминал cmd с правами администратора
2. Выполнить команду reg add HKLM\SOFTWARE\Classes\.md /v PerceivedType /t REG_SZ /d text
Управление ML проектами

Каждый раз, когда залетаю в ML-соревнование сталкиваюсь с одной и той же проблемой: через два-три дня активной работы над задачей генерируется с десяток новых файлов. Например, промежуточные CSV файлы, тестовые submission-ы и модели, jupyter блокноты и python-скрипты. Разбираться с этим через неделю становится крайне сложно: проблемы jupyter-ноутбуков известны и пока, кажется, ультимативного решения для них нет.

Возникла логичная мысль, что я не единственный, кто столкнулся с подобной проблемой. Покопавшись некоторое время в интернетах нашёл несколько интересных обсуждений и статей на Kaggle и Toward Data Science, которые навели меня на любопытную штуку: cookiecutter.

По сути это генератор шаблонного ML проекта с предопределённой файловой структурой: директория созданного проекта содержит поддиректории для моделей, данных (исходных и промежуточных), python-сценариев (для предобработки данных, генерации признаков и пр.), jupyter-ноутбуков, моделей и прочего.

Устанавливается cookiecutter стандартно — через pip:
pip install cookiecutter

Создать новый проект:
python -m cookiecutter https://github.com/drivendata/cookiecutter-data-science

GitHub репозиторий, передаваемый первым аргументом, — шаблон проекта.

По-умолчанию структура проекта выглядит следующим образом:
├───data
│ ├───external
│ ├───interim
│ ├───processed
│ └───raw
├───docs
├───models
├───notebooks
├───references
├───reports
│ └───figures
└───src
├───data
├───features
├───models
└───visualization

Пока применил на единственном соревновании и нашёл вещи, которые мне не нравятся (например, нет директории для submission-ов и конфигурационных файлов). Буду продолжать использовать и, возможно, сделаю заточенный под мои нужды шаблон.

#python #lib #tool
Форматированный вывод дат в Python

Сегодня заметка одной строкой для того, чтобы в следующий раз сразу знать где искать.

Наткнулся на крутой cheat sheet про форматированный вывод дат в Python — вещь, которую каждый раз приходится гуглить.

Сайт Python strftime cheatsheet содержит понятную шпаргалку по теме.

PS: На КДПВ только первая треть всей шпаргалки.

#python #cheatsheet
Форматированный вывод без лишних зависимостей

Ранее я писал про icecream — библиотеку для более удобных/красивых "отладочных" print-ов. Да, модуль классный и полезный, но тащить каждый раз его ради двух-трёх использований кажется излишним.

Для такого случая в Python 3.8 появился новый синтаксис для f-строк:
>>> int_field = 42
>>> print(f'{int_field=}')
int_field=42

>>> str_field = 'hello world!'
>>> print(f'{str_field=}')
str_field='hello world!'

При этом работает и со сложными типами:
>>> arr = list(range(5))
>>> print(f'{arr=}')
arr=[0, 1, 2, 3, 4]

>>> d = {'a': 1, 'b': 2}
>>> print(f'{d=}')
d={'a': 1, 'b': 2}


И даже с классами:
>>> class Foo:
>>> def __init__(self, field_1: str, field_2: int) -> None:
>>> self.field_1 = field_1
>>> self.field_2 = field_2
>>>
>>> def __repr__(self) -> str:
>>> return f'{self.field_1=}; {self.field_2=}'
>>>
>>> foo = Foo('string field', 146)
>>> print(f'{foo=}')

foo=self.field_1='string field'; self.field_2=146


К сожалению в Google Colab и Kaggle Notebooks используется Python 3.7. Так что в этот раз будет ссылка на неинтерактивную версию в DataLore.

Если зарегистрированы в DataLore, то можете потыкать по этой ссылке.

#python
Визуализация git diff

Сегодня в очередной раз возникла необходимость сравнить две ветки в git-репозитории.

По-старинке сделал git diff main..other-branch > main.diff получил файл на несколько сотен строк. Просматривать его в текстовом редакторе — не то, чем бы я хотел заниматься.

Порывшись в интернете буквально 5-10 минут наткнулся на сервис https://diffy.org. Сервис простой как железная дорога — кидаете в него diff, а он рисует по нему дерево. Именно то, что я искал.

Для примера сделал diff для двух веток scikit-learn. Полученное дерево можно посмотреть здесь — https://diffy.org/diff/04db055a56b82.

По-умолчанию ссылка живёт 24 часа, но срок жизни можно продлить. Этот diff будет доступен ещё около 80 часов.

#TIL
Канал жив, но был в анабиозе. Сегодняшним постом постараюсь вернуться к более активному ведению канала — запасы интересностей пополнялись все четыре месяца молчания.

Сегодня расскажу про любопытную штуку для Jupyter Notebook.

Для начала напомню, что у pip — менеджера пакетов в python — есть замечательная команда freeze, которая позволяет отобразить список установленных пакетов:
>>> pip freeze
accelerate==0.3.0
argon2-cffi==21.1.0
arrow==1.2.1
...
widgetsnbextension==3.5.1
xgboost==1.4.2
xxhash==2.0.2
В реальности команда выдала список из 127 установленных пакетов в виртуальном окружении (поставьте 😱, если не знаете про виртуальные окружения — мало ли).

Я вижу как минимум два сценария, как это можно использоваться:
1. сохранить список в requirements.txt для фиксации версий библиотек для потомков
2. отобразить список используемых библиотек и их версий в отчёте, написанном в Jupyter Notebook.

И для второго случая есть более удобная библиотека: watermark.

Библиотека добавляет magic-команду, которая позволяет отобразить список библиотек и их версии, но только тех, которые были импортированы в блокнот.

Преимущество, как мне кажется, очевидно: вместо списка из миллионов библиотек, покажем только те, которые действительно требуются для работы блокнота.

Например, в Google Colab предустановлено более 300 библиотек (можно посмотреть в примере), но в примере явно импортированы только 6 и остальные мне не важны.

Пользоваться максимально просто:
1. установить через pip install watermark
2. загрузить extension командой %load_ext watermark
3. выполнить magic-команды %watermark с необходимыми аргументами (весь список можно узнать через %watermark?)

Вывод выглядит значительно аккуратнее и не имеет лишнего:
>>> %watermark --iversions --machine --python
Python implementation: CPython
Python version : 3.7.13
IPython version : 5.5.0

Compiler : GCC 7.5.0
OS : Linux
Release : 5.4.188+
Machine : x86_64
Processor : x86_64
CPU cores : 2
Architecture: 64bit

skimage : 0.18.3
torch : 1.11.0+cu113
sklearn : 0.0
tensorflow: 2.8.2+zzzcolab20220527125636
IPython : 5.5.0
numpy : 1.21.6
pandas : 1.3.5

Пример в colab

#python #lib
Возведение в степень в Python

Сегодня открытие связанное с Python пришло совершенно с неожиданной стороны — с привычной операции возведения числа в некоторую степень: x ** y.

Как вы считаете, что вернёт следующий код -1 ** 2? В моём мире розовых пони эта операция банальна как железная дорога: ответ должен быть 1. Однако Python здесь со мной не согласен. По его мнению -1 ** 2 == -1.

Дело здесь в следующем. Степенное выражение вычисляется справа налево, а унарный оператор - учитывается в итоговом результате:
1. сначала возводится 1 во вторую степень
2. результат умножается на -1.

Есть три способа обойти эту особенность:
1. записать -1 в отдельную переменную: x = -1; x ** 2
2. добавить скобки, для повышения приоритета: (-1) ** 2
3. использовать встроенную функцию pow(-1, 2).

Примеры на colab

Ссылка на документацию Python

#TIL #python
NLP в три строчки кода

Неожиданно прилетела по работе задача: разделять текст на предложения.

На первый взгляд проблема не выглядит очень сложной, но в процессе изучения понимаешь, что ".", "!", "?" — не всегда значат конец предложения.

Текущая реализация использует SRX-правила, но решать эту задачу десятками эвристик кажется не рационально: сложно добавлять новые так, чтобы не сломалось предыдущие; для каждого нового языка следует с нуля набирать примеры и писать правила.

Быстрый поиск по github выдал, что хорошие люди уже запили библиотеку trankit, которая из коробки умеет решать эту и несколько других задач для 56 языков (в том числе русский, английский, китайский, вьетнамский и др.).

Trankit умеет рашать задачи Named Entity Recognition, Sentence Boundary Detection, Part-Of-Speech Recognition, Lemmatization. Для меня более важна вторая — Sentence Boundary Detection — но, возможно, кому-то будут интересны и другие. Интерфейс библиотеки прямой как железная дорога:

from trankit import Pipeline
# Инициализация пайплайна, который автоматически определяет язык
p = Pipeline('auto', gpu=True)

text = '''Входная дверь открылась, и к вошедшей маме тут же подбежали Арина и Настя.
— Мама, папа сказал, что мы завтра все вместе пойдём в парк!
— Нет, он сказал: «Девочки, я так рад, что мы завтра все вместе пойдём в парк, наконец-то!»
— Вот зануда! Какая разница, мы всё равно пойдём в парк, — Арина была недовольна.
'''
sentences = p.ssplit(text)
print(sentences)


Вывод:
{'text': 'Входная дверь открылась, и к вошедшей маме тут же подбежали Арина и Настя.\n— Мама, папа сказал, что мы завтра все вместе пойдём в парк!\n— Нет, он сказал: «Девочки, я так рад, что мы завтра все вместе пойдём в парк, наконец-то!»\n— Вот зануда! Какая разница, мы всё равно пойдём в парк, — Арина была недовольна.\n',
'sentences': [{'id': 1,
'text': 'Входная дверь открылась, и к вошедшей маме тут же подбежали Арина и Настя.',
'dspan': (0, 74)},
{'id': 2,
'text': '— Мама, папа сказал, что мы завтра все вместе пойдём в парк!',
'dspan': (75, 135)},
{'id': 3,
'text': '— Нет, он сказал: «Девочки, я так рад, что мы завтра все вместе пойдём в парк, наконец-то!»',
'dspan': (136, 227)},
{'id': 4, 'text': '— Вот зануда!', 'dspan': (228, 241)},
{'id': 5,
'text': 'Какая разница, мы всё равно пойдём в парк, — Арина была недовольна.',
'dspan': (242, 309)}],
'lang': 'russian'}


Пример специально взял непростой: с диалогом и прямой речью, т.к. эти штуки сбивают основанные на правилах алгоритмы.

Исходники, как всегда, можно найти в ноутбуке на Google Colab

PS: Trankit основан на широко используемой библиотеке для NLP transformers от Hugging Face

#python #lib #ml
Не одним лишь argparse

До недавнего времени я сходу мог назвать два способа работы с command line аргументами в Python:
1. вручную разбирать параметры из sys.argv:

import sys

def log():
args = sys.argv
print(args)

if __name__ == '__main__':
log()

2. использовать стандартный модуль argparse:

import argparse

def log():
parser = argparse.ArgumentParser()
parser.add_argument('--name')
args = parser.parse_args()
print(args)

if __name__ == '__main__':
log()

Однако, кроме argparse есть и другие библиотеки для разбора аргументов командной строки. Один из них `click`.

Чем click лучше argparse:
- поддержка разных типов параметров (в том числе файлов, путей в ФС, флагов, перечислений и т.д.)
- поддержка кастомных типов параметров
- на мой взгляд более понятный API
- поддержка prompt-аргументов
- поддержка переменных окружения
- etc.

Пример выше на click выглядит просто:

import click

@click.command()
@click.option('--name', type=str)
def log(name):
print(name)

if __name__ == '__main__':
log()

Больше примеров в google colab (но на самом colab-е проверить работу не получится — придётся копировать локально и экспериментировать).

#python #lib
Практически ровно год назад я решил сделать telegram-бота для сохранения файлов (в первую очередь книг) в облачное хранилище.

Для реализации бота взял самую популярную библиотеку для Python python-telegram-bot. В прошлый раз запала хватило только на реализацию аутентификацию в tg-боте через OAuth 2.0. Детали про хранение access и refresh токенов я решил оставить на потом. И это потом наконец-то наступило, но несколько в ином виде: здесь будет отвлечённый пример без OAuth и предыдущего кода бота для более простого изложения.

https://makesomecode.me/2022/11/telegram-bot-persistence/
Сегодня хотел бы поделиться с вами небольшой утилитой Wox, которой пользуюсь уже несколько лет и подсадил некоторое число людей.

Если у вас есть опыт работы с macOS, то, возможно, слышали об Alfred. Если нет, то эта аппка позволяет запускать приложения, искать файлы в ОС, выполнять рутинные задачи и автоматизировать работу.

Я исторически завязан на Windows, а Alfred кажется заманчивой штукой, чтобы не найти ему аналога на винде. И Wox — та самая альтернатива.

Замена на минималках — важных фич по автоматизации здесь нет. Но приложения запускает, файлы ищет и выполнять простые задачи может. К тому же можно легко сделать своё расширение на Python или C#. Например, я написал простейший плагин для конвертации валют с использованием внешнего API минут за 30. Из которых 10 потратил на поиски модуля wox.py (в gist-е уже всё в куче).

И да, по сравнению с Alfred — Wox полностью бесплатный и с исходным кодом на github

Вы, кстати, на чём сидите: Windows 👍, macOS 🌚 или Linux 😱?

#app #windows
Бывало ли у вас такое, что вам хотелось автоматизировать получение содержимого статьи из интернета? Например, из поста на Хабре забрать только саму статью и изображения к ней — без рекламы и разных "вам так же может быть интересна статья про автоматизацию дачного туалета"?

В 2010 году я писал клиента для Pocket для Windows Phone (пусть земля ему будет пухом). Тогда у меня впервые и возникла такая задача — отображать только содержимое статьи без лишнего мусора. В то время ничего приличного не существовало и предлагали использовать сторонний платный сервис, который упоминать здесь не буду, т.к. не занесли к делу не имеет прямого отношения. Если очень интересно — напишите в комментах и я добавлю ссылку.

Но на сегодня, как оказалось, уже всё придумано — библиотека newspaper.

Газетка, на самом деле, умеет значительно больше, чем просто "получить контент статьи по урлу". Например, с её помощью можно достать:
1. авторов статьи,
2. дату публикации,
3. КДПВ,
4. текст статьи,
5. ключевые слова (!),
6. саммари (!!),
7. распарсить целый сайт и т.д.

Лично пользовался только частью доступной функциональности, но либа выглядит как нечто, что точно надо иметь в виду на будущее.

Live demo, как всегда, на google colab

PS: Как и любая универсальная библиотека, не заточенная под конкретный сайт, newspaper работает не всегда идеально и в ноутбуке выше это отлично видно на примере заголовка статьи.

#python #lib
Прерву три месяца молчания новой интересной штукой, которую недавно нашёл.

Dalai позволяет запускать большие языковые модели (c) LLaMA и Alpaca на локальном компе. Это как ChatGPT, но скромнее. Но и требования к оперативке у Dalai значительно проще:
- 7B => ~4 GB
- 13B => ~8 GB
- 30B => ~16 GB
- 65B => ~32 GB

Сами модели занимают разумное место на диске: от 4Gb за 7B модель до 41Gb за квантизированную 65B модель. Полная 65B модель выходит за рамки приличия и отжирает 432Gb на диске.

Пока лично проверил только 7B модель на ноутбуке с 32Gb оперативы и довольно старым CPU (Core i7 7700). Работает не быстро, но работает (скриншот).

PS: Как минимум 7B модель не понимает русский язык