Антиспам бот Telegram на LSTM нейросети: пошаговое руководство | AiManual
AiManual Logo Ai / Manual.
28 Апр 2026 Гайд

Создаём антиспам-бота для Telegram на своей LSTM нейросети: пошаговое руководство

Создайте антиспам-бота для Telegram с нуля на своей LSTM нейросети. Сбор датасета, обучение, интеграция с TDLib. Полный код и примеры.

Спам — это раковая опухоль мессенджеров. Телеграм с его открытыми API и группами — идеальная среда для рассадника ботов, рекламирующих криптоскам, порно или «заработок в интернете». Стандартные фильтры Telegram работают так себе: регулярки и черные списки — моветон. Хотите реальную защиту? Нужна нейросеть. Своя. Обученная на своих данных. Именно это мы и сделаем.

В статье Как построить антиспам-бота для Telegram на Gemini Flash мы уже касались темы антиспама, но там использовалась LLM, которая требует запросов к API и денег. Наш подход — офлайн, дешево и сердито. Запускаете на своем сервере — никаких лимитов и подписок.

Почему LSTM, а не трансформеры или регулярки?

Регулярки ломаются на первой же обфускации. Трансформеры (BERT, GPT) — это удар по памяти и времени. LSTM — золотая середина. Она видит последовательность слов, улавливает паттерны вроде «заработай 100500$ за час» и при этом весит 50 МБ. На CPU инференс занимает 10–30 мс. Для бота с нагрузкой в тысячу сообщений в минуту — идеально.

Что нам понадобится?

  • Python 3.12+ (на момент 28.04.2026 актуальна версия 3.12, но 3.13 уже в релиз-кандидате)
  • PyTorch 2.6 или TensorFlow 2.17 — выберу PyTorch, он дает больше контроля
  • python-telegram-bot v21.5 — асинхронная обвязка
  • Datasets (Hugging Face) для загрузки готовых датасетов
  • scikit-learn, nltk, emoji для предобработки
  • Сервер (VPS с 2 ГБ RAM и 2 vCPU — за глаза)

Если вы никогда не писали ботов под Telegram, рекомендую сначала пройти курс Создание Telegram-бота — там разжёвана база.

Пошаговый план (чтобы не заблудиться)

1 Сбор датасета: как раздобыть чистые данные для обучения

В открытом доступе есть несколько размеченных коллекций SMS-спама. Но для Telegram они подходят плохо — там короткие сообщения, много эмодзи, URL и мусора. Лучше собрать свой датасет:

  • SMS Spam Collection (5572 сообщения) — как основа.
  • NUS SMS Corpus (около 1000 сообщений с метками).
  • Собственный сбор — выгрузите историю чатов через Telegram API (с согласия участников), разметьте вручную несколько тысяч сообщений.

Ошибка №1: не проверять дубликаты. Если одно и то же сообщение встретится и в тренировочной, и в тестовой выборке, точность будет липовой. Удалите дубликаты сразу.

import pandas as pd
from datasets import load_dataset

# Загружаем SMS Spam Collection
sms = load_dataset("sms_spam", split="train").to_pandas()
sms.columns = ["label", "text"]
sms["label"] = sms["label"].map({"ham": 0, "spam": 1})

# Добавляем свои собранные данные (колонки: text, label)
my_data = pd.read_csv("my_telegram_spam.csv")
dataset = pd.concat([sms, my_data], ignore_index=True)
dataset.drop_duplicates(subset="text", inplace=True)
print(dataset.shape)  # (6500, 2) - примерно

2 Предобработка: как не утопить модель в мусоре

Сырой текст нельзя скормить нейросети. Нужно:

  • Привести к нижнему регистру.
  • Заменить URL на токен <URL> — иначе модель выучит конкретные ссылки.
  • Удалить или заменить эмодзи на текстовое описание (библиотека emoji).
  • Лемматизировать (pymorphy2 для русского, nltk для английского).
  • Удалить стоп-слова, но не все — иногда спам использует «неестественные» стоп-слова для обхода фильтров.

Ошибка №2: полная очистка от пунктуации. В спаме часто используют дефисы, звёздочки, многоточия — они могут быть сигнатурой. Удаляйте только лишнее, оставляйте подозрительные символы.

import re
import emoji
import nltk
from nltk.corpus import stopwords

nltk.download('stopwords')

def clean_text(text: str, lang='english') -> str:
    text = text.lower()
    text = re.sub(r'https?://\S+', '', text)
    text = emoji.demojize(text)  # :joy: -> joy
    words = nltk.word_tokenize(text)
    stop = set(stopwords.words(lang))
    # оставляем короткие слова (они могут быть спам-ключевые)
    words = [w for w in words if w not in stop or len(w) <= 2]
    return ' '.join(words)

dataset['clean'] = dataset['text'].apply(clean_text)

3 Токенизация и создание эмбеддингов

Каждое слово превращаем в числовой индекс. Используем Tokenizer из Keras (или torchtext). Ограничиваем словарь 10000 самых частых токенов.

from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

MAX_WORDS = 10000
MAX_LEN = 100  # макс. длина сообщения
tokenizer = Tokenizer(num_words=MAX_WORDS)
tokenizer.fit_on_texts(dataset['clean'])
sequences = tokenizer.texts_to_sequences(dataset['clean'])
X = pad_sequences(sequences, maxlen=MAX_LEN, padding='post')
y = dataset['label'].values

4 Архитектура LSTM: почему без dropout — катастрофа

Берём Embedding слой, затем LSTM (лучше два слоя) и Dense с сигмоидой. Переобучение — бич маленьких датасетов. Dropout 0.5 между слоями спасает.

import torch
import torch.nn as nn

class SpamLSTM(nn.Module):
    def __init__(self, vocab_size, embedding_dim=128, hidden_dim=64):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embedding_dim)
        self.lstm1 = nn.LSTM(embedding_dim, hidden_dim, batch_first=True, bidirectional=True)
        self.dropout = nn.Dropout(0.5)
        self.lstm2 = nn.LSTM(hidden_dim * 2, hidden_dim, batch_first=True, bidirectional=True)
        self.fc = nn.Linear(hidden_dim * 2, 1)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        x = self.embedding(x)
        x, _ = self.lstm1(x)
        x = self.dropout(x)
        x, _ = self.lstm2(x)
        x = x[:, -1, :]  # только последний выход
        out = self.sigmoid(self.fc(x))
        return out.squeeze()

model = SpamLSTM(vocab_size=MAX_WORDS)

Если вам интересна тема семантической обработки текста в Telegram, рекомендую прочитать Семантический поиск в Telegram: разбираем кейс с Habr и строим свой аналог — там показано, как использовать эмбеддинги для поиска похожих сообщений.

5 Обучение: бинарная кросс-энтропия и дисбаланс классов

В обучающей выборке обычно 85–90% «не спам» и 10–15% «спам». Просто Accuracy не показатель. Используем weighted loss или Focal Loss. Я предпочитаю взвешенный BCE с весом обратно пропорционально частоте класса.

from sklearn.utils.class_weight import compute_class_weight
import numpy as np

class_weights = compute_class_weight('balanced', classes=np.unique(y), y=y)
weight_tensor = torch.tensor([class_weights[1]], dtype=torch.float32)
criterion = nn.BCEWithLogitsLoss(pos_weight=weight_tensor)

optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# Обучайте 10 эпох с early stopping

После обучения сохраняем модель и токенизатор — они понадобятся боту.

6 Интеграция с Telegram-ботом

Пишем асинхронного бота на python-telegram-bot. При получении сообщения прогоняем текст через предобработку и модель. Если вероятность спама > 0.7 — удаляем или отправляем на премодерацию.

from telegram.ext import Application, MessageHandler, filters
import joblib

tokenizer = joblib.load('tokenizer.pkl')
model = torch.jit.load('spam_model.pt', map_location='cpu')
model.eval()

def predict(text: str) -> bool:
    clean = clean_text(text)
    seq = tokenizer.texts_to_sequences([clean])
    padded = pad_sequences(seq, maxlen=MAX_LEN, padding='post')
    with torch.no_grad():
        prob = model(torch.tensor(padded)).item()
    return prob > 0.7

async def handle_message(update, context):
    if predict(update.message.text):
        await update.message.delete()
        # можно еще забанить отправителя на время
    else:
        pass

app = Application.builder().token("YOUR_TOKEN").build()
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
app.run_polling()

Не используйте блокирующий код (time.sleep) в асинхронном обработчике — похороните пропускную способность. Всё предсказание делайте в отдельном потоке через run_in_executor или используйте Torch в инференсе асинхронно.

7 Деплой и мониторинг

Запускаем бота на VPS под systemd. Добавляем метрики: количество удалённых спам-сообщений, ложные срабатывания (если есть кнопка «Это не спам»). Регулярно дообучайте модель на новых данных — боты адаптируются.

Возможные ошибки и как их избежать

  • Переобучение на коротких сообщениях. Одно слово типа «квратира» может оказаться спамом, а «квартира» — нет. Добавьте аугментацию: заменяйте случайные символы.
  • Игнорирование эмодзи. Спамеры любят эмодзи для привлечения внимания. Демоджизируйте, а не удаляйте — иначе потеряете сигнал.
  • Пренебрежение кешированием токенизатора. Не грузите токенизатор заново для каждого сообщения — сохраните в глобальной переменной.
  • Нет гибридной защиты. LSTM не панацея. Добавьте белые списки доверенных пользователей и капчу для новых.

Кстати, если вы захотите пойти дальше и автоматизировать не только фильтрацию, но и общение с пользователями, взгляните на статью Генеративный ИИ-бот для заказов: как сделать, чтобы он не разговаривал, а работал — там про создание продуктивного бота.

💡
Совет: не пытайтесь сразу натренировать модель на идеальную точность. Лучше сделайте гибрид: нейросеть + белые списки + капча. И никогда не доверяйте датасету, собранному вручную — половина «спама» окажется обычными спорами.

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

А напоследок: LSTM — это динозавр, но динозавр, который до сих пор способен уделать новомодные модели на узкой задаче классификации, если подойти с умом. Соберите датасет, подберите гиперпараметры, и ваш чат будет очищен от спама на 95%+. Остальные 5% — всегда оставляйте для пользовательских жалоб, чтобы учить модель дальше.

Подписаться на канал