PyTorch torchtext

Хотя экосистема PyTorch имеетtorchvisionобработку данных изображений,torchaudioобработку аудиоданных, но в области обработки текста официально предоставленнаяtorchtextбиблиотека претерпела некоторые изменения. В этом разделе рассказывается, как выполнять предварительную обработку текстовых данных, построение словаря, загрузку данных и другие операции с использованием различных методов.

Примечание: библиотека torchtext подверглась некоторому рефакторингу. Рекомендуется использоватьtorchtext.legacyили создать собственный конвейер обработки текста. Последняя версия torchtext вернулась и предоставляет более современный API.


1. Основы предварительной обработки текстовых данных

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

1.1 Базовый конвейер обработки текста

Пример

import re
from collections import Counter

class SimpleTokenizer:
    """
Простой токенизатор: токенизирует по пробелам и знакам препинания
    """

    def __init__(self):
        # Сопоставление знаков препинания
        self.punctuation = str.maketrans('', '', '.,!?;:"\'-()[]{}')

    def tokenize(self, text):
        # Преобразовать в нижний регистр
        text = text.lower()
        # Удалить знаки препинания
        text = text.translate(self.punctuation)
        # Токенизировать
        tokens = text.split()
        return tokens


class Vocabulary:
    """
Построение словаря
    """

    def __init__(self, min_freq=2, max_size=10000):
        self.min_freq = min_freq
        self.max_size = max_size
        self.word2idx = {'<PAD>': 0, '<UNK>': 1}
        self.idx2word = {0: '<PAD>', 1: '<UNK>'}
        self.word_count = Counter()

    def build_vocab(self, texts):
        """Построить словарь из списка текстов"""
        tokenizer = SimpleTokenizer()

        # Подсчет частот слов
        for text in texts:
            tokens = tokenizer.tokenize(text)
            self.word_count.update(tokens)

        # Построить словарь
        for word, count in self.word_count.most_common(self.max_size):
            if count < self.min_freq:
                break
            if word not in self.word2idx:
                idx = len(self.word2idx)
                self.word2idx[word] = idx
                self.idx2word[idx] = word

        print(f"Размер словаря: {len(self.word2idx)}")

    def encode(self, text, max_len=50):
        """Закодировать текст как последовательность индексов"""
        tokenizer = SimpleTokenizer()
        tokens = tokenizer.tokenize(text)[:max_len]

        # Кодирование
        indices = [
            self.word2idx.get(token, self.word2idx['<UNK>'])
            for token in tokens
        ]

        # Дополнить нулями
        if len(indices) < max_len:
            indices += [self.word2idx['<PAD>']] * (max_len - len(indices))

        return indices

    def decode(self, indices):
        """Декодировать последовательность индексов в текст"""
        tokens = [self.idx2word.get(idx, '<UNK>') for idx in indices]
        return ' '.join(tokens)


# Пример использования
texts = [
    "Привет, мир",
    "Это тест",
    "PyTorch отлично подходит для глубокого обучения",
    "Обработка естественного языка — это увлекательно",
    "Глубокое обучение позволяет реализовать множество приложений",
]

vocab = Vocabulary(min_freq=1, max_size=100)
vocab.build_vocab(texts)

# Кодирование
encoded = vocab.encode("Привет, мир глубокого обучения!", max_len=10)
print(f"Закодированный результат: {encoded}")

# Декодирование
decoded = vocab.decode(encoded)
print(f"Результат декодирования: {decoded}")

2. Использование torchtext (Legacy)

Если torchtext установлен, можно использовать legacy-модуль для обработки текста.

2.1 Установка и импорт

Пример

# Установка torchtext
# pip install torchtext

# Импорт legacy-модуля
try:
    import torchtext
    from torchtext.legacy import data
    from torchtext.legacy import datasets
    print(f"версия torchtext: {torchtext.__version__}")
    TORCHTEXT_AVAILABLE = True
except ImportError:
    print("torchtext не установлен, используется собственная реализация")
    TORCHTEXT_AVAILABLE = False

# Или попробуйте новую версию
try:
    from torchtext import transforms
    from torchtext.datasets import AG_NEWS
    print("Доступна новая версия torchtext")
except ImportError:
    print("Новая версия torchtext недоступна")

2.2 Field и Dataset

Пример

# Используйте torchtext.legacy
from torchtext.legacy import data
from torchtext.legacy import datasets
from torchtext.legacy.data import Field, TabularDataset, BucketIterator

# Определите поля
TEXT = Field(
    tokenize='spacy',           # Использовать spacy для токенизации
    tokenizer_language='en_core_web_sm',
    lower=True,                 # Преобразовать в нижний регистр
    include_lengths=True,       # Вернуть длину последовательности
    batch_first=True            # Размер пакета первым
)

LABEL = Field(
    sequential=False,
    use_vocab=False,
    dtype=torch.long
)

# Определить набор данных
# Предполагается формат CSV-файла: label,text
# 1,Это положительный отзыв
# 0,Это отрицательный отзыв

# Или используйте встроенный набор данных (например, IMDB)
# train_data, test_data = datasets.IMDB.splits(TEXT, LABEL)

print("Настройка Field и Dataset завершена")

2.3 Построение словаря и итераторы

Пример

# Построить словарь
# TEXT.build_vocab(train_data, vectors="glove.6B.100d", max_size=10000)

# Создать итератор
# train_iterator, test_iterator = BucketIterator.splits(
#     (train_data, test_data),
#     batch_size=32,
#     sort_within_batch=True,
#     device=torch.device('cuda')
# )

# Итерация обучения
# for batch in train_iterator:
#     text, lengths = batch.text
#     labels = batch.label
# # Код обучения

print("Настройка словаря и итератора завершена")

3. Пользовательский конвейер данных NLP

Рекомендуется использовать более гибкий пользовательский подход к обработке данных, не зависящий от torchtext.

3.1 Реализация класса Dataset

Пример

import torch
from torch.utils.data import Dataset, DataLoader
from collections import Counter
import re

class TextDataset(Dataset):
    """
Текстовый датасет
    """

    def __init__(self, texts, labels, vocab=None, max_len=128, min_freq=2):
        self.texts = texts
        self.labels = labels
        self.max_len = max_len

        # Создайте или используйте существующий словарь
        if vocab is None:
            self.vocab = self._build_vocab(texts, min_freq)
        else:
            self.vocab = vocab

    def _build_vocab(self, texts, min_freq):
        """Построить словарь"""
        word_counts = Counter()

        for text in texts:
            tokens = self._tokenize(text)
            word_counts.update(tokens)

        # Создать словарь
        word2idx = {'<PAD>': 0, '<UNK>': 1}
        for word, count in word_counts.items():
            if count >= min_freq:
                word2idx[word] = len(word2idx)

        return word2idx

    def _tokenize(self, text):
        """Простая токенизация"""
        # Преобразовать в нижний регистр
        text = text.lower()
        # Удалить специальные символы, оставить буквенно-цифровые символы и пробелы
        text = re.sub(r'[^a-z0-9\s]', ' ', text)
        # Токенизировать
        tokens = text.split()
        return tokens

    def _encode(self, text):
        """Закодировать текст"""
        tokens = self._tokenize(text)[:self.max_len]
        indices = [
            self.vocab.get(token, self.vocab['<UNK>'])
            for token in tokens
        ]

        # Дополнить нулями
        if len(indices) < self.max_len:
            indices += [self.vocab['<PAD>']] * (self.max_len - len(indices))

        return indices

    def __len__(self):
        return len(self.texts)

    def __getitem__(self, idx):
        text = self.texts[idx]
        label = self.labels[idx]

        encoded = self._encode(text)
        return torch.tensor(encoded, dtype=torch.long), torch.tensor(label, dtype=torch.long)


# Пример использования
texts = [
    "Это отличный фильм",
    "Я ненавидел этот фильм",
    "Потрясающая игра",
    "Ужасная игра",
    "Настоятельно рекомендую",
]

labels = [1, 0, 1, 0, 1]

dataset = TextDataset(texts, labels, max_len=10)
print(f"Размер набора данных: {len(dataset)}")
print(f"Размер словаря: {len(dataset.vocab)}")

# Получить пример
text, label = dataset[0]
print(f"Кодировка текста: {text[:5]}...")
print(f"Метка: {label}")

3.2 Продвинутые токенизаторы

Пример

# Использовать spacy для продвинутой токенизации
def tokenize_with_spacy(text):
    """Токенизировать с помощью spacy"""
    try:
        import spacy
        nlp = spacy.load("en_core_web_sm")
        doc = nlp(text)
        return [token.text for token in doc]
    except ImportError:
        print("spacy не установлен, используется простая токенизация")
        return text.lower().split()


# Использовать nltk для токенизации и стемминга
def tokenize_with_nltk(text):
    """Токенизировать с помощью nltk"""
    try:
        import nltk
        from nltk.tokenize import word_tokenize
        from nltk.stem import PorterStemmer

        # Токенизировать
        tokens = word_tokenize(text.lower())

        # Необязательно: стемминг
        stemmer = PorterStemmer()
        tokens = [stemmer.stem(token) for token in tokens if token.isalnum()]

        return tokens
    except ImportError:
        return text.lower().split()


# Используйте SentencePiece для субсловной токенизации
class SentencePieceTokenizer:
    """
Используйте SentencePiece для субсловной токенизации
    """

    def __init__(self, model_path=None):
        self.model_path = model_path
        self.sp = None

    def train(self, texts, vocab_size=10000):
        """Обучить модель SentencePiece"""
        try:
            import sentencepiece as spm
            import tempfile

            # Записать во временный файл
            with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f:
                for text in texts:
                    f.write(text + '\n')
                temp_file = f.name

            # Обучить
            spm.SentencePieceTrainer.train(
                input=temp_file,
                model_prefix='spm_model',
                vocab_size=vocab_size,
                character_coverage=1.0,
                model_type='unigram',
            )

            self.sp = spm.SentencePieceProcessor()
            self.sp.load('spm_model.model')

        except ImportError:
            print("sentencepiece не установлен")

    def encode(self, text):
        if self.sp:
            return self.sp.encode(text, out_type=str)
        return text.split()

    def decode(self, ids):
        if self.sp:
            return self.sp.decode(ids)
        return ' '.join(ids)

3.3 Полный загрузчик данных

Пример

import torch
from torch.utils.data import Dataset, DataLoader
from collections import Counter

class NLPDataset(Dataset):
    """
Полный класс набора данных NLP
Поддерживает последовательности переменной длины, построение словаря и дополнение батчей
    """

    def __init__(self, texts, labels, min_freq=2, max_vocab=30000):
        self.texts = texts
        self.labels = labels
        self.min_freq = min_freq

        # Построить словарь
        self.word2idx, self.idx2word = self._build_vocab(texts, max_vocab)
        self.pad_idx = self.word2idx['<PAD>']
        self.unk_idx = self.word2idx['<UNK>']

    def _tokenize(self, text):
        text = text.lower()
        text = re.sub(r'[^a-z0-9\s]', ' ', text)
        return text.split()

    def _build_vocab(self, texts, max_vocab):
        """Построить словарь"""
        counter = Counter()
        for text in texts:
            tokens = self._tokenize(text)
            counter.update(tokens)

        # Построить словарь
        word2idx = {'<PAD>': 0, '<UNK>': 1}
        idx2word = {0: '<PAD>', 1: '<UNK>'}

        for word, count in counter.most_common(max_vocab):
            if count >= self.min_freq and word not in word2idx:
                idx = len(word2idx)
                word2idx[word] = idx
                idx2word[idx] = word

        return word2idx, idx2word

    def __len__(self):
        return len(self.texts)

    def __getitem__(self, idx):
        text = self.texts[idx]
        tokens = self._tokenize(text)
        indices = [self.word2idx.get(t, self.unk_idx) for t in tokens]

        return {
            'input': indices,
            'label': self.labels[idx],
            'length': len(indices)
        }


def collate_fn(batch, pad_idx=0):
    """
Пользовательская collate-функция: обработка последовательностей переменной длины
    """

    # Сортировать по длине в порядке убывания
    batch.sort(key=lambda x: x['length'], reverse=True)

    texts = [item['input'] for item in batch]
    labels = [item['label'] for item in batch]
    lengths = [item['length'] for item in batch]

    # Дополнить до одинаковой длины
    max_len = max(lengths)
    padded = [text + [pad_idx] * (max_len - len(text)) for text in texts]

    return {
        'input': torch.tensor(padded, dtype=torch.long),
        'labels': torch.tensor(labels, dtype=torch.long),
        'lengths': torch.tensor(lengths, dtype=torch.long)
    }


# Использовать DataLoader
def create_dataloader(texts, labels, batch_size=32, shuffle=True):
    """Создать загрузчик данных"""
    dataset = NLPDataset(texts, labels)
    pad_idx = dataset.pad_idx

    dataloader = DataLoader(
        dataset,
        batch_size=batch_size,
        shuffle=shuffle,
        collate_fn=lambda b: collate_fn(b, pad_idx)
    )

    return dataloader, dataset


# Имитация данных
texts = [
    "Я люблю этот фильм",
    "Это плохой фильм",
    "Отличный фильм",
    "Ужасная игра",
    "Рекомендую это",
    "Не стоит смотреть",
]

labels = [1, 0, 1, 0, 1, 0]

dataloader, dataset = create_dataloader(texts, labels, batch_size=2)

for batch in dataloader:
    print("Форма входных данных:", batch['input'].shape)
    print("Метки:", batch['labels'])
    print("Длины:", batch['lengths'])
    print("---")

4. Загрузка предобученных векторов слов

Использование предобученных векторов слов может улучшить производительность модели.

4.1 Векторы слов GloVe

Пример

import torch
import numpy as np

def load_glove_vectors(filepath, word2idx, embedding_dim=300):
    """
Загрузить предобученные векторы слов GloVe

Параметры:
filepath: путь к файлу GloVe
word2idx: словарь
embedding_dim: размерность векторов слов
    """

    print("Загрузка векторов слов GloVe...")

    # Инициализация матрицы эмбеддингов
    num_words = len(word2idx)
    embeddings = np.random.randn(num_words, embedding_dim).astype(np.float32)
    embeddings[word2idx['<PAD>']] = np.zeros(embedding_dim)  # Установить вектор PAD в ноль

    words_loaded = 0

    with open(filepath, 'r', encoding='utf-8') as f:
        for line in f:
            values = line.strip().split()
            word = values[0]

            if word in word2idx:
                idx = word2idx[word]
                vector = np.asarray(values[1:], dtype=np.float32)
                if len(vector) == embedding_dim:
                    embeddings[idx] = vector
                    words_loaded += 1

    print(f"Загружено {words_loaded}/{num_words} векторов слов")

    return torch.from_numpy(embeddings)


# Имитация загрузки
def create_pretrained_embedding(word2idx, embedding_dim=300, pretrained_path=Отсутствует):
    """
Создать слой предобученных эмбеддингов
    """

    if pretrained_path and False:  # Установите True и укажите допустимый путь
        weights = load_glove_vectors(pretrained_path, word2idx, embedding_dim)
    else:
        # Использовать случайную инициализацию
        num_words = len(word2idx)
        weights = torch.randn(num_words, embedding_dim) * 0.1

    embedding = torch.nn.Embedding.from_pretrained(weights, padding_idx=0)
    return embedding


# Пример
word2idx = {'<PAD>': 0, '<UNK>': 1, 'hello': 2, 'world': 3, 'test': 4}
embedding = create_pretrained_embedding(word2idx, embedding_dim=100)

print(f"Форма слоя эмбеддингов: {embedding.weight.shape}")

4.2 Загрузка Word2Vec с помощью gensim

Пример

def load_word2vec(word2idx, model_path=None):
    """
Загрузка Word2Vec с помощью gensim
    """

    try:
        from gensim.models import KeyedVectors

        # Загрузить модель
        # model = KeyedVectors.load_word2vec_format(model_path, binary=True)

        # Создать матрицу эмбеддингов
        embedding_dim = 300
        num_words = len(word2idx)
        embeddings = np.random.randn(num_words, embedding_dim).astype(np.float32)

        # Заполнить векторы слов
        embeddings[word2idx['<PAD>']] = np.zeros(embedding_dim)

        # Загрузить векторы слов
        # for word, idx in word2idx.items():
        # if word in model:
        # embeddings[idx] = model[word]

        return torch.from_numpy(embeddings)

    except ImportError:
        print("gensim не установлен")
        return None

4.3 Пользовательские предобученные эмбеддинги

Пример

import torch
import torch.nn as nn

class PretrainedEmbedding(nn.Module):
    """
Слой эмбеддингов, поддерживающий загрузку предобученных векторов слов
    """

    def __init__(self, vocab_size, embed_dim, pretrained_weights=Отсутствует,
                 padding_idx=0, freeze=False):
        super().__init__()

        self.embedding = nn.Embedding(
            vocab_size,
            embed_dim,
            padding_idx=padding_idx
        )

        # Загрузить предобученные веса
        if pretrained_weights is not None:
            self.embedding.weight.data.copy_(pretrained_weights)

        # Замораживать ли
        if freeze:
            self.embedding.weight.requires_grad = False

    def forward(self, x):
        return self.embedding(x)


# Пример использования
vocab_size = 10000
embed_dim = 300
pretrained = torch.randn(vocab_size, embed_dim) * 0.1

embedding_layer = PretrainedEmbedding(
    vocab_size,
    embed_dim,
    pretrained_weights=pretrained,
    padding_idx=0,
    freeze=False  # Установите True, чтобы заморозить слой эмбеддингов
)

#
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
output = embedding_layer(x)
print(f"Форма входа: {x.shape}")
print(f"Форма вывода: {output.shape}")

5. Аугментация текстовых данных

Аугментация данных может улучшить способность модели к обобщению.

5.1 Аугментация обратным переводом

Пример

# Аугментация обратным переводом требует API перевода; ниже приведён концептуальный пример

def back_translation_augment(text, translator):
    """
Аугментация обратным переводом: перевести текст на другой язык, а затем перевести его обратно
    """

    # Перевести на целевой язык
    translated = translator.translate(text, dest='fr')
    # Перевести обратно на исходный язык
    back_translated = translator.translate(translated.text, dest='en')
    return back_translated.text

# Пример использования
# from googletrans import Translator
# translator = Translator()
# augmented_text = back_translation_augment("Я люблю этот фильм", translator)

5.2 Замена синонимов

Пример

import random

def synonym_replacement(text, n=1, stop_words=None):
    """
Замена синонимами
    """

    if stop_words is None:
        stop_words = {'a', 'an', 'the', 'is', 'are', 'was', 'were'}

    words = text.lower().split()
    replaceable = [i for i, w in enumerate(words) if w not in stop_words]

    if not replaceable:
        return text

    n = min(n, len(replaceable))
    replace_idx = random.sample(replaceable, n)

    # Упрощённое сопоставление синонимов (на самом деле следует использовать WordNet)
    synonym_map = {
        'good': ['great', 'excellent', 'nice'],
        'bad': ['poor', 'terrible', 'awful'],
        'фильм': ['кинофильм', 'кино'],
        'любил': ['обожал', 'наслаждался'],
    }

    for idx in replace_idx:
        word = words[idx]
        if word in synonym_map:
            words[idx] = random.choice(synonym_map[word])

    return ' '.join(words)


# Пример использования
text = "Я любил этот хороший фильм"
augmented = synonym_replacement(text, n=2)
print(f"Исходное предложение: {text}")
print(f"Аугментированное: {augmented}")

5.3 Случайная вставка и удаление

Пример

import random

def random_insertion(text, n=1):
    """
Случайная вставка: случайным образом вставлять слова в текст
    """

    words = text.lower().split()
    for _ in range(n):
        if len(words) > 1:
            idx = random.randint(0, len(words) - 1)
            # Упрощённые вставляемые слова
            insert_words = ['really', 'very', 'quite', 'actually']
            words.insert(idx, random.choice(insert_words))
    return ' '.join(words)


def random_deletion(text, p=0.1):
    """
Случайное удаление: удалять слова с вероятностью p
    """

    words = text.lower().split()
    if len(words) == 1:
        return text

    remaining = [w for w in words if random.random() > p]
    if len(remaining) == 0:
        return random.choice(words)

    return ' '.join(remaining)


def random_swap(text, n=1):
    """
Случайная перестановка: случайным образом поменять местами позиции двух слов
    """

    words = text.lower().split()
    if len(words) < 2:
        return text

    for _ in range(n):
        idx1, idx2 = random.sample(range(len(words)), 2)
        words[idx1], words[idx2] = words[idx2], words[idx1]

    return ' '.join(words)


# Пример использования
text = "Это отличный фильм о любви и счастье"

print("Original:", text)
print("Вставлено:", random_insertion(text, n=2))
print("Удалено:", random_deletion(text, p=0.2))
print("Переставлено:", random_swap(text, n=2))

6. Примеры распространённых задач NLP

6.1 Полный конвейер классификации текста

Пример

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader

# 1. Подготовка данных
texts = [
    "Мне нравится этот продукт, он потрясающий!",
    "Ужасное качество, пустая трата денег.",
    "Отличное соотношение цены и качества.",
    "Не удовлетворён покупкой.",
    "Лучшая покупка в моей жизни!",
    "Очень разочарован этим товаром.",
]

labels = [1, 0, 1, 0, 1, 0]  # 1: положительный, 0: отрицательный

# 2. Набор данных
class TextClassificationDataset(Dataset):
    def __init__(self, texts, labels, max_len=50):
        self.texts = texts
        self.labels = labels
        self.max_len = max_len
        self.vocab = self._build_vocab(texts)

    def _build_vocab(self, texts):
        vocab = {'<PAD>': 0, '<UNK>': 1}
        for text in texts:
            for word in text.lower().split():
                if word not in vocab:
                    vocab[word] = len(vocab)
        return vocab

    def __len__(self):
        return len(self.texts)

    def __getitem__(self, idx):
        text = self.texts[idx]
        label = self.labels[idx]

        # Простое кодирование
        tokens = text.lower().split()[:self.max_len]
        indices = [self.vocab.get(t, 1) for t in tokens]

        # Дополнение
        if len(indices) < self.max_len:
            indices += [0] * (self.max_len - len(indices))

        return torch.tensor(indices, dtype=torch.long), torch.tensor(label, dtype=torch.long)

# 3. Модель
class TextClassifier(nn.Module):
    def __init__(self, vocab_size, embed_dim=128, hidden_dim=64, num_classes=2):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
        self.lstm = nn.LSTM(embed_dim, hidden_dim, batch_first=True)
        self.fc = nn.Linear(hidden_dim, num_classes)

    def forward(self, x):
        embedded = self.embedding(x)
        _, (hidden, _) = self.lstm(embedded)
        output = self.fc(hidden.squeeze(0))
        return output

# 4. Обучение
dataset = TextClassificationDataset(texts, labels)
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)

model = TextClassifier(vocab_size=len(dataset.vocab))
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

# Обучение в течение нескольких эпох
model.train()
for epoch in range(10):
    total_loss = 0
    for texts_batch, labels_batch in dataloader:
        optimizer.zero_grad()
        outputs = model(texts_batch)
        loss = criterion(outputs, labels_batch)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()

    print(f"Epoch {epoch+1}, Loss: {total_loss/len(dataloader):.4f}")

print("Обучение завершено!")

6.2 Пример разметки последовательностей (NER)

Пример

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader

# Набор данных для разметки последовательностей
class NERDataset(Dataset):
    """
Набор данных для распознавания именованных сущностей
Формат: [(word, tag), ...]
    """

    def __init__(self, sentences_tags, word2idx, tag2idx, max_len=50):
        self.sentences_tags = sentences_tags
        self.word2idx = word2idx
        self.tag2idx = tag2idx
        self.max_len = max_len

    def __len__(self):
        return len(self.sentences_tags)

    def __getitem__(self, idx):
        sentence_tags = self.sentences_tags[idx]
        words = [wt[0] for wt in sentence_tags]
        tags = [wt[1] for wt in sentence_tags]

        # Кодирование
        word_ids = [self.word2idx.get(w.lower(), 1) for w in words[:self.max_len]]
        tag_ids = [self.tag2idx[t] for t in tags[:self.max_len]]

        # Дополнение
        if len(word_ids) < self.max_len:
            word_ids += [0] * (self.max_len - len(word_ids))
            tag_ids += [0] * (self.max_len - len(tag_ids))

        return {
            'words': torch.tensor(word_ids, dtype=torch.long),
            'tags': torch.tensor(tag_ids, dtype=torch.long)
        }

# NER-модель
class NERModel(nn.Module):
    def __init__(self, vocab_size, tag_size, embed_dim=128, hidden_dim=256):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
        self.lstm = nn.LSTM(embed_dim, hidden_dim, batch_first=True, bidirectional=True)
        self.fc = nn.Linear(hidden_dim * 2, tag_size)

    def forward(self, x):
        embedded = self.embedding(x)
        lstm_out, _ = self.lstm(embedded)
        logits = self.fc(lstm_out)
        return logits


# Пример данных
data = [
    [("John", "B-PER"), ("lives", "O"), ("in", "O"), ("New", "B-LOC"), ("York", "I-LOC"), (".", "O")],
    [("Apple", "B-ORG"), ("is", "O"), ("a", "O"), ("company", "O"), (".", "O")],
]

# Построить словарь слов и таблицу тегов
word2idx = {'<PAD>': 0, '<UNK>': 1}
tag2idx = {'O': 0, 'B-PER': 1, 'I-PER': 2, 'B-LOC': 3, 'I-LOC': 4, 'B-ORG': 5, 'I-ORG': 6}

for sent in data:
    for word, tag in sent:
        if word.lower() not in word2idx:
            word2idx[word.lower()] = len(word2idx)

print(f"Размер словаря: {len(word2idx)}")
print(f"Количество тегов: {len(tag2idx)}")

7. Рекомендуемые альтернативы

7.1 Другие библиотеки обработки текста

Имя библиотеки Особенности Применимые сценарии
HuggingFace Datasets Загрузка и обработка данных Общие задачи NLP
spacy Продвинутые инструменты NLP Токенизация, NER, синтаксический анализ зависимостей
NLTK Классические инструменты NLP Обучение, прототипирование
textblob Простота и удобство использования Быстрая обработка текста

7.2 Использование HuggingFace Datasets

Пример

# Используйте библиотеку HuggingFace Datasets
# pip install datasets

from datasets import load_dataset

# Загрузка набора данных
# dataset = load_dataset("imdb")
# dataset = load_dataset("squad")

# Предварительная обработка данных
# def preprocess_function(examples):
# return tokenizer(examples['text'], truncation=True, padding='max_length')

# tokenized_dataset = dataset.map(preprocess_function, batched=True)

print("Пример использования HuggingFace Datasets:")
print("from datasets import load_dataset")
print("dataset = load_dataset('imdb')")

8. Краткий справочник по API

8.1 Общие операции обработки текста

Действие Метод
Токенизация text.split(), spacy, nltk, jieba
Построение словаря Counter, vocab.build_vocab()
Кодирование vocab.encode(), tokenizer.encode()
Декодирование vocab.decode(), tokenizer.decode()
Внутренний отступ pad_sequence, collate_fn
Загрузка векторов слов Прямая загрузка Gensim, GloVe

8.2 Методы аугментации данных

Метод Описание
Замена синонимами Случайная замена слов синонимами
Случайная вставка Случайная вставка слов
Случайное удаление Случайное удаление слов
Случайная перестановка Случайная перестановка позиций слов
Обратный перевод Перевести и затем перевести обратно

8.3 Рекомендуемый конвейер обработки данных

1. 数据清洗
   - 去除 HTML 标签、特殊字符
   - 统一编码、大小写

2. 分词
   - 英文: spaCy / NLTK
   - 中文: jieba / pkuseg

3. 词表构建
   - 过滤低频词
   - 设定最大词表大小

4. 序列化
   - 编码为索引
   - 统一长度(填充/截断)

5. 数据增强(可选)
   - 同义词替换、回译

6. 批量加载
   - DataLoader + 自定义 collate_fn
Другие расширения