NLP

Как превратить гуманитарную курсовую в GitHub-проект

Курсовая сдана, оценка получена, файл 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.

Нашему проекту хватило четырёх библиотек:

  • razdel — режет текст на предложения по правилам русского языка (в отличие от NLTK и spaCy, сделан специально под русский);
  • pandas — таблицы: собрать данные, сгруппировать, посчитать средние;
  • matplotlib — графики;
  • jupyter — движок ноутбука.

Если код в курсовой уже был (пусть даже скопированный из туториалов) — половина дела сделана. Если не было, самое время пересчитать хотя бы часть выводов скриптом: например, вручную посчитанные частотности слов.

Как создать первый репозиторий

Репозиторий — это папка вашего проекта, за которой следит Git: он запоминает каждое сохранённое состояние, чтобы к нему можно было вернуться. GitHub — место в интернете, где эта папка лежит и куда вы отправляете обновления. Всё остальное — детали поверх этой картинки.

Самый первый шаг делается вообще без командной строки:

  1. Заведите бесплатный аккаунт на github.com.
  2. Нажмите зелёную кнопку New (или +New repository в правом верхнем углу).
  3. Придумайте название репозитория. Оно должно быть короткое, написано латиницей и через дефис (у нас этоchekhov-brevity). Напишите описание в одну строку (Sentence length in Russian prose: Chekhov and three contemporaries), поставьте галочку Add a README file, и нажмите Create repository.

Всё, репозиторий существует. Пустой, но настоящий: у него есть адрес, который уже можно кому-то отправить.

Дальше эту папку в интернете нужно связать с папкой на компьютере — скачать репозиторий к себе (эта операция называется Clone). Терминал для этого не обязателен: то же самое кнопками вместо команд делают GitHub Desktop и встроенная в VS Code панель Source Control.

Что важно понять про эту связку: работаете вы всегда с папкой на своём компьютере, а GitHub хранит её связанную копию в облаке. Туда вы отправляете изменения, оттуда проект скачивают другие.

Третьим шагом, ещё до того, как в папку попадёт хоть один файл с данными, создаём .gitignore — список того, что в репозиторий пускать нельзя.

От «Введения» к README.md

README.md — первое, что видит человек, открывший репозиторий. По сути это то же «Введение» из курсовой, только переписанное для другого читателя. Читатель README не обязан знать вашу дисциплину, зато хочет за тридцать секунд понять: что здесь, зачем и как это запустить.

Схема переписывания примерно такая:

  • «Актуальность темы». Здесь достаточно одного абзаца о том, какой вопрос решает проект и почему это интересно.
  • «Цели и задачи» превращаются в раздел What this project does: одно предложение о том, что проект делает. У нас «сравнивает среднюю длину предложения у Чехова и трёх современников».
  • «Материалы исследования» становятся разделом Data: откуда взят корпус, сколько в нём текстов, где он лежит.
  • «Методы» остаются Methods: их описывают коротко и  со ссылками на библиотеки.
  • Новое, чего в курсовой не было: How to run — как установить зависимости и запустить код, а еще  главные результаты, лучше сразу с картинкой-графиком.

README обычно пишут на английском. Англоязычное описание резко расширяет аудиторию проекта.

README.md нашего репозитория

Данные: приводим корпус в порядок

Для репозитория данные стоит привести к стандартному виду. Несколько правил, которые стоит соблюдать:

  • Используйте простые форматы. Тексты лучше хранить в .txt (кодировка UTF-8), таблицы — в .csv. Не .docx и не .xlsx: их сложно обрабатывать кодом и невозможно нормально сравнивать между версиями.
  • Давайте файлам осмысленные имена. blok_poem_1912.txt лучше, чем Новый документ (17).txt.
  • Заведите файл с описанием данных, где указано, что означает каждая колонка таблицы, откуда взяты тексты, как размечались категории. В data science это называется data dictionary или datasheet, и его отсутствие — самая частая причина, по которой чужим датасетом невозможно пользоваться.
  • Храните сырые и обработанные данные отдельно. Папка data/raw не редактируется никогда; всё, что вы чистили и преобразовывали, лежит в data/processed, и путь от первого ко второму воспроизводится скриптом.
  • Соблюдайте авторские права. Если корпус собран из современных текстов, выкладывать их целиком может быть нельзя. Вместо этого можно выложить скрипт для сбора текстов, либо только производные данные: частотные списки, метаданные, эмбеддинги.

Наш корпус

В нашем случае собирать тексты самим незачем: русская классика давно оцифрована. Подходит 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.

Jupyter Notebook против «голого» кода

Можно выложить просто скрипты .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")

Результат

Запускаем ноутбук.

ПроизведенийСредняя длинаМедиана
Чехов7311,0 слова10,8
Горький2511,6 слова11,0
Брюсов3014,5 слова14,0
Толстой4215,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
Файл .gitignore, открытый в VS Code

Виртуальное окружение и кэш незачем тащить в репозиторий, потому что любой развернёт их у себя из вашего 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

Теперь, когда мы закончили анализ, мы можем заполнить README по схеме из начала статьи. 

Что дальше

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

  • Сравнение с другими авторами
  • Другие метрики: длина слова, доля сложноподчинённых предложений, глубина синтаксического дерева и так далее.
  • Порядок в коде. Когда кода в ноутбуке становится много, механику (загрузку файлов, подсчёты) выносят в отдельный файл в папке src/.

Каждый из этих пунктов будет новым коммитом в тот же репозиторий.

Чек-лист перед публикацией

Прежде чем давать кому-то ссылку, пройдитесь по списку:

  • Репозиторий создан, у него осмысленное имя и краткое описание.
  • В корне лежит README.md: что делает проект, какие данные, как запустить.
  • Данные приведены в порядок: простые форматы (.txt, .csv), понятные имена файлов, есть описание колонок, сырые и обработанные данные разведены.
  • Есть файл с описанием данных, и в нём записано, что вошло в выборку, а что нет и почему.
  • С авторскими правами всё чисто: если корпус нельзя выкладывать целиком, лежит скрипт для сбора или только производные данные.
  • Основной анализ оформлен ноутбуком, интерпретация — в markdown-ячейках рядом с графиками.
  • Ноутбук перезапущен целиком (Restart & Run All) и считается сверху вниз без ошибок.
  • Есть requirements.txt (или pyproject.toml) — проект разворачивается одной командой.
  • Есть .gitignore, и в репозиторий не утекли окружение, кэш и секреты (.env).
  • Структура папок аккуратная, сообщения коммитов осмысленные.
  • В README есть разделы How to run и Limitations.
  • Вы открыли репозиторий в режиме постороннего человека и проверили: понятно ли, что это и как запустить.

Вместо заключения

Репозиторий из курсовой — это способ сделать результаты исследования доступными не только для преподавателя, но и для других людей. Вместо файла, который после защиты обычно остаётся лежать в папке «Учёба», вы получаете полноценный проект, который можно показывать в портфолио и развивать дальше.

В резюме ссылка на GitHub часто говорит о ваших навыках больше, чем строка «написала курсовую работу». Её можно отправить рекрутеру, или будущему научному руководителю: им гораздо проще за несколько минут просмотреть README, ноутбук с анализом и структуру проекта, чем читать десятки страниц в формате Word. Базовые команды Git и работа с Jupyter Notebook осваиваются за пару вечеров, а аккуратно оформленный репозиторий останется частью вашего профессионального портфолио и сможет пригодиться ещё не раз.

Ссылки

Git и GitHub

Jupyter

Библиотеки для NLP

  • NLTK
  • spaCy
  • pymorphy3
  • Natasha — набор Python-библиотек для русского NLP
  • razdel — сегментация русского текста на предложения и токены

Данные и таблицы

  • pandas
  • SciPy
  • RusLit — корпус русской классики, использованный в примере
  • Datasheets for Datasets — методология описания датасетов

Окружение и зависимости

  • pip — управление пакетами Python
  • uv — документация Astral

Оформление репозитория

Share

Recent Posts

Тест: какая профессия в NLP вам подходит?

Кажется, что NLP — это только про код, математику и нейросети, а гуманитариям тут искать нечего. Но на самом деле…

01.09.2026

Как новый африканский алфавит пробился в Unicode: цифровая жизнь адлама

В 1989 году два брата из Гвинеи, которым было всего 14 и 10 лет, решили придумать письменность для родного языка.…

26.08.2026

Российский суд не признал копирайт на сгенерированную Джоконду

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

24.08.2026