Курсовая сдана, оценка получена, файл kursovaya_final_FINAL2.docx лежит в папке «Учёба». Всё это можно превратить в проект на GitHub, который будет работать на вас: его можно показать на собеседовании, добавить в резюме, прислать научному руководителю в магистратуре. Работодатели в digital humanities и data-командах смотрят на GitHub чаще, чем на диплом. В этой статье пошагово разберем, как собрать такой проект.
Иллюстрация: Света Нагаева
«Системный Блокъ» уже писал о том, какие практики из машинного обучения стоит использовать при работе с текстами, теперь мы предлагаем гайд, как разместить гуманитарное исследование на GitHub.
Представим, что часть вашей курсовой посвящена особенностям стиля Чехова. Вам хочется подтвердить свои тезисы не только качественными аргументами, но и количественно. «Краткость — сестра таланта» — этот знаменитый афоризм писатель сформулировал в письме брату Александру. Вопрос напрашивается сам собой: следовал ли Чехов собственному правилу? Короче ли его предложения, чем у современников?
Этот вопрос хорош тем, что для него легко подобрать метрику: мы будем измерять среднюю длину предложения. Кейс довольно прост, особые навыки программирования здесь не потребуются.
Чтобы ответить на поставленный вопрос и при этом сделать из исследования GitHub-проект, вам пригодятся:
Git и GitHub. Git — это система контроля версий: она запоминает историю изменений ваших файлов. Об её установке читайте в следующем разделе. GitHub — сайт, где эти файлы хранятся и где их видят другие люди. Достаточно освоить четыре команды: git add (отметить изменённые файлы), git commit (сохранить «снимок» проекта), git push (отправить на GitHub), git pull (забрать свежую версию). Всё остальное можно гуглить по мере необходимости — так делают и профессиональные разработчики.
Jupyter Notebook. Среда, где код, его результаты и обычный текст живут в одном документе. Для гуманитарной работы это идеальный формат.
Python-библиотеки. Для анализа текстов чаще всего хватает связки: pandas (таблицы), matplotlib (графики), NLTK или spaCy (токенизация, леммы, части речи; для русского языка стоит посмотреть на pymorphy3 и Natasha), scipy — если в работе есть статистические тесты. Ставить всё это удобнее не по одной библиотеке, а разом через файл requirements.txt.
Нашему проекту хватило четырёх библиотек:
Если код в курсовой уже был (пусть даже скопированный из туториалов) — половина дела сделана. Если не было, самое время пересчитать хотя бы часть выводов скриптом: например, вручную посчитанные частотности слов.
Репозиторий — это папка вашего проекта, за которой следит Git: он запоминает каждое сохранённое состояние, чтобы к нему можно было вернуться. GitHub — место в интернете, где эта папка лежит и куда вы отправляете обновления. Всё остальное — детали поверх этой картинки.
Самый первый шаг делается вообще без командной строки:
Всё, репозиторий существует. Пустой, но настоящий: у него есть адрес, который уже можно кому-то отправить.
Дальше эту папку в интернете нужно связать с папкой на компьютере — скачать репозиторий к себе (эта операция называется Clone). Терминал для этого не обязателен: то же самое кнопками вместо команд делают GitHub Desktop и встроенная в VS Code панель Source Control.
Что важно понять про эту связку: работаете вы всегда с папкой на своём компьютере, а GitHub хранит её связанную копию в облаке. Туда вы отправляете изменения, оттуда проект скачивают другие.
Третьим шагом, ещё до того, как в папку попадёт хоть один файл с данными, создаём .gitignore — список того, что в репозиторий пускать нельзя.
README.md — первое, что видит человек, открывший репозиторий. По сути это то же «Введение» из курсовой, только переписанное для другого читателя. Читатель README не обязан знать вашу дисциплину, зато хочет за тридцать секунд понять: что здесь, зачем и как это запустить.
Схема переписывания примерно такая:
README обычно пишут на английском. Англоязычное описание резко расширяет аудиторию проекта.
Для репозитория данные стоит привести к стандартному виду. Несколько правил, которые стоит соблюдать:
В нашем случае собирать тексты самим незачем: русская классика давно оцифрована. Подходит RusLit: русская классика в .txt, кодировка UTF-8. В папке prose/ лежат Чехов, Толстой, Достоевский, Тургенев, Гоголь, Горький, Брюсов, Герцен, Лермонтов, Пушкин. В каждой папке есть info.csv с колонками name,year.
Обратите внимание, что датасет уже соответствует правилам, о которых мы говорили: простые форматы, осмысленные имена файлов. Таких аккуратно собранных корпусов в открытом доступе меньше, чем хотелось бы, и это первое, на что стоит смотреть при выборе.
RusLit — чужой датасет со своим репозиторием, поэтому мы не кладём его к себе. В .gitignore прописана строчка data/raw/, а в README — одна команда, которой корпус скачивается:
git clone —depth 1 https://github.com/d0rj/RusLit.git data/raw
Папку data/raw заранее создавать не нужно, git заведёт её сам. —depth 1 значит «только последняя версия, без истории изменений».
Давайте сравним Чехова с Горьким и Брюсовым — единственными его современниками в датасете — и с Толстым, который писал поколением раньше и в крупной форме.
Тексты у авторов очень разные по объёму: у Толстого есть «Война и мир» на десятки тысяч предложений, а есть рассказы на пару сотен. Если просто свалить все предложения в одну кучу и посчитать среднее, Толстого будет представлять почти одна «Война и мир»: на неё приходится треть всех его предложений в датасете, а на четыре самых длинных текста — половина. Чтобы этого избежать, нужно считать по произведениям. Берём каждый текст отдельно, считаем среднюю длину предложения внутри него и сравниваем авторов уже по этим числам.
Также в датасете есть пьесы: «Три сестры» и «Вишнёвый сад» у Чехова, а у Горького — «Мещане» и «На дне». Кроме пьес у Горького мы находим публицистику и поэму в прозе. Всё это другой тип литературы, поэтому эти тексты из анализа мы исключаем.
Что взяли, что выбросили и почему — всё это идёт в data/DATA.md.
Можно выложить просто скрипты .py, и для утилит так и стоит делать. Но основной анализ гуманитарного проекта лучше оформить ноутбуком, потому что ноутбук читается как статья: постановка вопроса, код, график, абзац интерпретации — и так до выводов.
Разница видна сразу. «Голый» скрипт молча сохраняет figure1.png в папку, и читателю нужно самому запустить код, найти картинку и догадаться, что она значит. В ноутбуке график появляется прямо под ячейкой, а под ним ваш текст — интерпретация, он же комментарий.
Этот текст является отдельной ячейкой формата Markdown. Ноутбук состоит из ячеек двух типов: code — с кодом и его результатом, и markdown — с обычным форматированным текстом (заголовки, списки, курсив, ссылки, формулы). Именно markdown-ячейки превращают ноутбук из «кода с картинками» в связный рассказ: в них удобно сформулировать исследовательский вопрос перед блоком кода, а после графика — разобрать, что видно. Тот же язык, кстати, вы уже использовали в README и в описании данных, так что учить отдельно ничего не придётся.
GitHub отображает ноутбуки прямо в браузере, так что читателю даже не нужно ничего устанавливать, чтобы посмотреть ваш анализ.
Весь пайплайн собран в ноутбуке .analysis.ipynb: первые ячейки подключают библиотеки (включая NLP-сегментатор razdel) и задают параметры выборки — список авторов, читаемые метки для графиков и критерии отсева нерелевантных текстов.
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from razdel import sentenize
# Корпус лежит в data/raw (скачан командой git clone)
RAW = Path("data/raw/prose")
AUTHORS = ["Chekhov", "Gorky", "Bryusov", "Tolstoy"]
LABELS = {"Chekhov": "Чехов", "Gorky": "Горький",
"Bryusov": "Брюсов", "Tolstoy": "Толстой"}
# Считаем только повествовательную прозу: пьеса, публицистика, очерк и поэма
# в прозе — другой тип речи. Исключаем поимённо, с причиной у каждого текста.
EXCLUDED = {
("Chekhov", "Три сестры"): "пьеса",
("Chekhov", "Вишнёвый сад"): "пьеса",
("Gorky", "Мещане"): "пьеса",
("Gorky", "На дне"): "пьеса",
("Gorky", "Несвоевременные мысли"): "публицистика",
("Gorky", "Город жёлтого дьявола"): "очерк",
("Gorky", "Сказки об Италии"): "очерк",
("Gorky", "Человек"): "поэма в прозе",
}
# Короткие тексты не берём: на паре десятков предложений среднее — шум.
MIN_SENTENCES = 50 Длину предложений считает функция в три строки:
def sentence_lengths(text):
"""Длины предложений в словах."""
return [len(s.text.split()) for s in sentenize(text) if s.text.split()] sentenize из библиотеки razdel позволяет нам правильно разделить текст на предложения, так как делить текст просто по точкам не получится: разбиение ломается на сокращениях («и т. д.») и инициалах — «А. П. Чехов» превратился бы в три предложения.
Дальше — сборка таблицы. Здесь же в коде видно решение считать по произведениям: одна строка таблицы — один текст.
rows = []
for author in AUTHORS:
for path in sorted((RAW / author).glob("*.txt")):
title = path.stem
if (author, title) in EXCLUDED:
continue
lengths = sentence_lengths(path.read_text(encoding="utf-8"))
if len(lengths) < MIN_SENTENCES:
continue
rows.append({
"author": author,
"title": title,
"n_sentences": len(lengths),
"mean_sentence_len": round(sum(lengths) / len(lengths), 2),
})
works = pd.DataFrame(rows)
# Сохраняем результат. Папку data/processed код создаёт сам, если её нет.
out = Path("data/processed")
out.mkdir(parents=True, exist_ok=True)
works.to_csv(out / "works.csv", index=False, encoding="utf-8") Запускаем ноутбук.
| Произведений | Средняя длина | Медиана | |
|---|---|---|---|
| Чехов | 73 | 11,0 слова | 10,8 |
| Горький | 25 | 11,6 слова | 11,0 |
| Брюсов | 30 | 14,5 слова | 14,0 |
| Толстой | 42 | 15,3 слова | 15,0 |
Ну что же, Чехов действительно самый «краткий» из четырёх. Выводы правда надо делать осторожно, тест наш очень упрощённый!
Каждая точка соответствует одному произведению, черта — среднему по автору.
Как и в любом исследовании, в нашем проекте есть ряд ограничений: тексты взяты из сети, диалоги смешаны с описаниями, а доступные произведения все ещё отличаются по жанрам, периодам творчества и количеству книг.
Все эти пункты нужно внести в будущий раздел Limitations в README.
Наконец, несколько вещей, по которым опытный человек за минуту отличает аккуратный проект от свалки файлов.
Структура папок. Вот какой каркас получился у нас:
chekhov-brevity/
├── README.md вопрос, данные, метод, как запустить, результат
├── requirements.txt библиотеки с версиями
├── .gitignore что НЕ выкладывать
├── analysis.ipynb сам анализ
├── data/
│ ├── DATA.md описание данных
│ ├── raw/ скачанный RusLit, в репозиторий не входит
│ └── processed/
│ └── works.csv результат: одна строка — одно произведение
└── figures/
└── sentence_length.png результат: график Если код перерастает ноутбук, к этому списку добавляется папка src/ для вспомогательных скриптов, а сами ноутбуки переезжают в notebooks/.
requirements.txt. Список библиотек с версиями (pandas==2.2.0), чтобы любой человек мог развернуть ваше окружение одной командой pip install -r requirements.txt. Получить список из своего окружения можно командой pip freeze, но лучше оставить в файле только то, что проект действительно использует. У нас файл занимает четыре строки:
razdel==0.5.0
pandas==2.2.3
matplotlib==3.9.2
jupyter==1.1.1 Перед установкой стоит завести виртуальное окружение — изолированную песочницу для пакетов этого проекта:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt После source .venv/bin/activate в начале строки терминала появится (.venv) — значит, вы внутри песочницы. Если pip не находится, попробуйте pip3 или python3 -m pip.
.gitignore содержит информацию о том, что НЕ выкладывать. В репозиторий попадает не всё подряд: часть файлов туда лучше не пускать вовсе. Для этого рядом с README кладут файл .gitignore — простой список того, что Git должен игнорировать. Строчка в нём — это шаблон имени; всё, что под него подходит, в коммиты не попадёт. Вот весь наш файл:
# Сырые тексты не коммитим: это чужой датасет, он качается командой
data/raw/
# Окружение (если вы его создали) и кэш
.venv/
__pycache__/
*.pyc
# Секреты
.env
# Служебное
.ipynb_checkpoints/
.DS_Store Виртуальное окружение и кэш незачем тащить в репозиторий, потому что любой развернёт их у себя из вашего requirements.txt.
Секреты же, ключи к внешним сервисам (например, к API для сбора текстов) принято хранить в секрете, а не в коде, в отдельном файле .env, и именно его нельзя коммитить: ключ, однажды попавший в публичную историю, считается скомпрометированным, даже если вы удалите его следующим коммитом, он останется в истории. Файл .gitignore создавайте в самом начале, до первого git add: проще не пустить лишнее в репозиторий, чем вычищать его потом.
Осмысленные сообщения коммитов. Коммит — это «снимок» проекта, а сообщение — подпись к нему. Add stopword filtering for lemmatized corpus — хорошо. Наконец работает — плохо (хотя история коммитов любого живого проекта содержит пару таких). Правило простое: сообщение отвечает на вопрос «что изменится, если применить этот коммит».
Отправка изменений — это те самые команды из начала статьи:
git status # ← посмотреть глазами, что видит git
git add .
git commit -m "Add sentence length comparison for Chekhov and Tolstoy"
git push В GitHub Desktop и в панели Source Control редактора VS Code то же самое делается кнопками: сообщение в поле сверху, Commit, затем Sync Changes.
Теперь, когда мы закончили анализ, мы можем заполнить README по схеме из начала статьи.
Первая версия готова. Если вы вдруг решили продолжить такой проект, вот идеи, что могло бы пойти в следующие коммиты:
Каждый из этих пунктов будет новым коммитом в тот же репозиторий.
Прежде чем давать кому-то ссылку, пройдитесь по списку:
Репозиторий из курсовой — это способ сделать результаты исследования доступными не только для преподавателя, но и для других людей. Вместо файла, который после защиты обычно остаётся лежать в папке «Учёба», вы получаете полноценный проект, который можно показывать в портфолио и развивать дальше.
В резюме ссылка на GitHub часто говорит о ваших навыках больше, чем строка «написала курсовую работу». Её можно отправить рекрутеру, или будущему научному руководителю: им гораздо проще за несколько минут просмотреть README, ноутбук с анализом и структуру проекта, чем читать десятки страниц в формате Word. Базовые команды Git и работа с Jupyter Notebook осваиваются за пару вечеров, а аккуратно оформленный репозиторий останется частью вашего профессионального портфолио и сможет пригодиться ещё не раз.
Git и GitHub
Jupyter
Библиотеки для NLP
Данные и таблицы
Окружение и зависимости
Оформление репозитория
Кажется, что NLP — это только про код, математику и нейросети, а гуманитариям тут искать нечего. Но на самом деле…
В 1989 году два брата из Гвинеи, которым было всего 14 и 10 лет, решили придумать письменность для родного языка.…
Предприниматель требовал компенсацию за использование изображений, которые он сделал с помощью нейросети, но суд отказал