[Текстовая классификация] Краткое изложение методов, основанных на глубоком обучении

NLP
[Текстовая классификация] Краткое изложение методов, основанных на глубоком обучении

Введение

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

Вообще говоря, задача классификации текстов относится к классификации текстов в одну или несколько категорий в данной системе классификации. Объекты, подлежащие классификации, — это короткие тексты, такие как предложения, заголовки, обзоры продуктов и т. д., и длинные тексты, такие как статьи. Система классификации обычно делится вручную, например: 1) политика, спорт, военные 2) положительная энергия, отрицательная энергия 3) положительная, нейтральная, отрицательная. Кроме того, существует многоуровневая классификация текста, например, метка блога может быть: обработка естественного языка, классификация текста и т. д. Поэтому соответствующие режимы классификации можно разделить на:двухклассный,Мультикласса такжеМногоуровневая классификацияпроблема.

Задачи классификации текста в основном делятся на:

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

  • категории новостей: Система классификации новостей может помочь пользователям получать интересную информацию в режиме реального времени. Идентификация темы новостей на основе интересов пользователя и рекомендации по связанным новостям - два основных применения классификации новостей.

  • Тематический анализ: Тематический анализ пытается автоматически извлечь смысл из текстов, определяя их темы. Цель классификации тем — назначить одну или несколько тем каждому документу для облегчения анализа.

  • Система ответов на вопросы: Существует два типа систем ответов на вопросы: экстрактивные и генеративные. Извлекающий QA можно рассматривать как частный случай классификации текста. Имея вопрос и набор вариантов ответов, правильно классифицируйте каждый вариант ответа в соответствии с требованиями вопроса.

  • вывод на естественном языке: NLI, также известный как распознавание текстового следования (RTE), предсказывает, можно ли вывести значение одного текста из другого текста. В частности, системе необходимо присвоить метку каждой паре текстовых единиц, например следствию, противоречию и нейтральности.

Как правило, существует три основных подхода к классификации текстов:

  • Метод, основанный на сопоставлении характеристик правил (например, оценка эмоций на основе специальных слов, таких как «нравится», «ненависть» и т. д., но с низкой точностью, обычно как метод, помогающий судить)

  • Методы, основанные на традиционном машинном обучении (конструирование признаков + алгоритмы классификации)

image.png

  • Метод на основе глубокого обучения (вектор слов + нейронная сеть)

Классификация текстов на основе методов глубокого обучения

FastText

бумага:АР Вест V.org/ABS/1607.01…
Код:GitHub.com/Facebook Рес…

FastTextЭто инструмент для расчета векторов слов и классификации текста, открытый Facebook в 2016 году, который характеризуется высокой скоростью обучения. В задаче классификации текстаFastText(Мелкие сети) часто достигают точности, сравнимой с глубокими сетями, но на порядки быстрее, чем глубокие сети, по времени обучения. На стандартном многоядерном процессоре векторы слов в корпусе из 1 миллиарда слов могут быть обучены в течение 10 минут, а более 500 000 предложений с более чем 300 000 категорий могут быть классифицированы в течение 1 минуты.

FastTextЭто быстрый алгоритм классификации текста, который имеет два основных преимущества перед алгоритмами классификации на основе нейронных сетей:

  1. FastTextУскоряет обучение и тестирование, сохраняя при этом высокую точность
  2. FastTextНет необходимости в предварительно обученных векторах слов,FastTextОбучит вектор слова сам по себе
  3. FastTextДве важные оптимизации:Hierarchical Softmax,N-gram

FastTextтиповая архитектура иword2vecсерединаCBOWочень похоже, разница естьFastTextпредсказывать метки, покаCBOWПрогнозы — это промежуточные слова, то есть архитектура моделей похожа, но задачи у моделей разные:

word2vecПреобразование контекстных отношений в задачи мультиклассификации.В общих текстовых данных тезаурус колеблется от десятков тысяч до миллионов.Нереально напрямую обучать мультиклассовую логистическую регрессию во время обучения.word2vecДва метода оптимизации для крупномасштабных задач множественной классификации представлены вNegative samplingиHierarchical softmax. В оптимизации,Negative samplingОбновляются только векторы слов небольшого числа отрицательных слов, что снижает объем вычислений.Hierarchical softmaxТезаурус представляется в виде префиксного дерева, а путь от корня к листу может быть представлен в виде последовательности бинарных классификаторов.Сложность расчета одной мультиклассификации составляет отV|V|опущен на высоту дереваlog2Vlog_2 |V|.

FastTextАрхитектура модели: гдеw1,w2,,wn1,wnw_1,w_2,\ldots,w_{n-1},w_nзначит в текстеn-gramвектор, каждая функция является средним значением векторов слов.

image.png

слабость это:

Я не люблю такие фильмы, но этот понравился.

Я люблю такие фильмы, но не этот.

Такие два предложения абсолютно одинаковы, когда они отправляются в однослойную нейронную сеть после усреднения вектора слов. Классификатор не может различить разницу между двумя предложениями. Только путем добавленияn-gramХарактеристики могут отличаться позже. Поэтому в практическом применении необходимо иметь достаточное представление о данных, а затем выбирать модель.

Для сцен с длинным текстом и высокими требованиями к скоростиFasttextдаBaselineПредпочтительно. В то же время его также хорошо использовать для обучения векторов слов на неконтролируемых корпусах для текстового представления. Однако для дальнейшего улучшения эффекта требуются более сложные модели.

Структура модели

image.png

  1. Ввод модели:[batch_size, seq_len]

  2. embeddingСлой: случайная инициализация, размер вектора словаembed_size: [batch_size, seq_len, embed_size]

  3. просить обо всемseq_lenСреднее количество слов:[batch_size, embed_size]

  4. Полностью подключен +softmaxНормализованный:[batch_size,num_class]

основной код

class FastText(nn.Module):
    def __init__(self, config, word_embedding, freeze):
        """
        config.class_num: 类别数
        word_embedding: 训练好的词向量(一个类,包含idx2str、str2idx、vectors)
        freeze: 是否冻结词向量
        """
        super(FastText, self).__init__()
        word_embedding = word_embedding
        self.embedding_size = len(word_embedding.vectors[0])
        # 加载词向量
        self.embedding = nn.Embedding.from_pretrained(torch.from_numpy(np.asarray(word_embedding.vectors)),
                                                      freeze=freeze)
        self.fc = nn.Linear(self.embedding_size, config.class_num)

    def forward(self, input_ids):
        # text = [batch size, sent len]
        embedded = self.embedding(input_ids).float()
        # embedded = [batch size, sent len, emb dim]
        pooled = F.avg_pool2d(embedded, (embedded.shape[1], 1)).squeeze(1)
        # pooled = [batch size, embedding_dim]
        return self.fc(pooled), pooled

TextCNN

бумага:АР Вест V.org/ABS/1408.58…
Код:GitHub.com/Ю и Ким/CNN…

TextCNNЭто модель, предложенная Юн Кимом в 2014 году, которая впервые применилаCNNкодированиеn-gramПервая в своем роде функция.

image.png

Подробный процесс:

  • Embedding: первый слой самый левый на картинке5×35 \times 3Матрица предложения , каждая строка представляет собой вектор слов, а размерность33, которые можно сравнить с исходными пикселями изображения.

  • Convolution: затем послеkernel_sizes=(2,3,4) 1D сверточные слои, каждыйkernel_sizeимеют22выходыchannel.

  • MaxPolling: Третий этаж1-max poolingслой (потому что это измерение времени, также называемоеmax-over-time pooling), так что предложения разной длины проходят черезpoolingЗатем слой можно превратить в представление фиксированной длины.

  • Full Connection and Softmax: Последний слой полностью связанsoftmaxслой, который выводит вероятность каждого класса.

существуетTextCNNНа практике есть много мест для оптимизации:

  • kernel_sizesразмер: Этот параметр определяет извлечениеn-gramДлина признака, этот параметр в основном связан с данными, средняя длина5050Если внутри, используйте1010Следующее в порядке, в противном случае это может быть дольше. Вы можете сначала использовать размер при настройке параметровgrid search, найдите оптимальный размер, затем попробуйте сочетание оптимального размера и ближайших размеров

  • Filterномер: этот параметр влияет на размер конечного элемента. Если размер слишком велик, скорость обучения будет ниже. здесь, в100600100-600Вы можете настроить параметры между

  • CNNактивационная функция:Можешь попытатьсяReLU,tanh

  • Регуляризация: укажи вправоCNNРегуляризация параметров, вы можете использоватьdropoutилиL2, но эффект очень маленький, можно попробовать небольшойdropout

  • PoolingСпособ: выбираем по ситуацииmean,max,k-max pooling,большую часть времениmaxПроизводительность очень хорошая, потому что задача классификации не требует высокой детализации семантики, и фиксируется только самый крупный признак.

  • EmbeddingТаблица: можно выбрать китайскийcharилиwordВвод уровня также можно использовать для обоих, что улучшит эффект. Если обучающих данных достаточно (10w+10w+), так же можно тренироваться с нуля

  • ДистилляцияBERTизlogits, используя неконтролируемые данные в домене

TextCNNочень подходитКороткая и средняя текстовая сценасильныйbaseline,ноНе подходит для длинных текстов, так как размер ядра свертки обычно не очень большой,Невозможно захватить объекты на большом расстоянии. В то же время max-pooling также имеет ограничения, теряя структурную информацию текста, поэтому сложно найти в тексте сложные закономерности, такие как поворотные отношения. . Кроме того, если хорошенько подумать,TextCNNи традиционныеn-gramСуть модели мешка слов та же, и большая часть ее положительного эффекта связана с введением векторов слов, которые решают проблему разреженности модели мешка слов.

Структура модели

image.png

основной код

class TextCNN(nn.Module):
    def __init__(self, config, word_embedding, freeze):
        """
        config.n_filters: 卷积核个数(对应2维卷积的通道数)
        config.filter_sizes: 卷积核的多个尺寸
        config.class_num: 类别数
        config.dropout: dropout率
        word_embedding: 训练好的词向量(一个类,包含idx2str、str2idx、vectors)
        freeze: 是否冻结词向量
        """
        super(TextCNN, self).__init__()
        word_embedding = word_embedding
        self.embedding_size = len(word_embedding.vectors[0])
        self.embedding = nn.Embedding.from_pretrained(torch.from_numpy(np.asarray(word_embedding.vectors)),
                                                      freeze=freeze)
        self.convs = nn.ModuleList(
            [nn.Conv2d(in_channels=1, out_channels=config.n_filters, 
                       kernel_size=(fs, self.embedding_size)) for fs in config.filter_sizes])
        self.fc = nn.Linear(len(config.filter_sizes) * config.n_filters, config.class_num)
        self.dropout = nn.Dropout(config.dropout)

    def forward(self, input_ids):
        # text = [batch size, sent len]
        embedded = self.embedding(input_ids)
        # embedded = [batch size, sent len, emb dim]
        embedded = embedded.unsqueeze(1).float()
        # embedded = [batch size, 1, sent len, emb dim]
        conved = [F.relu(conv(embedded)).squeeze(3) for conv in self.convs]
        # conved_n = [batch size, n_filters, sent len - filter_sizes[n] + 1]
        pooled = [F.max_pool1d(conv, int(conv.shape[2])).squeeze(2) for conv in conved]
        # pooled_n = [batch size, n_filters]
        cat = self.dropout(torch.cat(pooled, dim=1))
        # cat = [batch size, n_filters * len(filter_sizes)]
        return self.fc(cat)

DPCNN

бумага:Love.Tencent.com/Arab/Media…
Код:GitHub.com/649453932/C…

ACLВ середине 2017 года TencentAI-labпридумалDeep Pyramid Convolutional Neural Networks for Text Categorization(DPCNN). В статье предлагается метод, основанный наword-levelуровень сети из-заTextCNNМеждугородные зависимости текста не могут быть получены с помощью свертки, а бумагаDPCNNПостоянно углубляя сеть, можно извлечь долгосрочные текстовые зависимости. Эксперименты показывают, что наилучшую точность можно получить, увеличив глубину сети без слишком больших вычислительных затрат. ‍

image.png

Region embedding

Автор будетTextCNNРезультат свертки сверточного слоя, содержащего многоразмерные сверточные фильтры, называетсяRegion embedding, что означает, что для текстовой области/фрагмента (например,3-gram), созданный после набора операций сверткиembedding.

Компромисс между сверточной и полносвязной

производитьregion embeddingПосле этого, по классическомуTextCNNМетод состоит в том, чтобы выбрать наиболее репрезентативные функции из каждой карты функций, то есть напрямую применить глобальный слой максимального объединения, чтобы сгенерировать вектор функций этого текста, если сверточный фильтрsizeимеют(3,4,5)эти трое, каждыйsizeВключают100100Ядро свертки, то конечно выдаст3*1003*100карты объектов, а затемmax-over-time-poolingОперация применяется к каждой карте объектов, поэтому вектор признаков текста равен3*100=3003*100=300измерение.

TextCNNСмысл этого в основном такой же, как у классической модели классификации текста модели мешка слов, ноone-hotприбытьword embeddingПреобразование представления позволяет избежать проблемы разреженности данных, с которой сталкиваются модели мешка слов.TextCNNВ сущности, синонимы, возникающие при введении словесных векторов, представляются подобными векторамиbonus,в то же времяTextCNNЭто может лучше использовать синонимические отношения в векторе слова.

Информация, передаваемая на большие расстояния, которую трудно усвоить в классической модели,TextCNNвсе еще трудно учиться.

изометрическая свертка

Предположим, что длина входной последовательности равнаnn, размер ядра сверткиmm, размер шагаss, заполнение на обоих концах входной последовательностиppнулей, то выходная последовательность сверточного слоя имеет вид(nm+2p)s+1\frac{(n-m+2p)}{s}+1.

  1. Узкая свертка: шагs=1s=1, оба конца не заполнены нулями, т.е.p=0p=0, выходная длина после свертки равнаnm+1n-m+1.
  2. Широкая свертка: шагs=1s=1, с нулями на обоих концахp=m1p=m-1, выходная длина после сверткиn+m1n+m-1.
  3. Свертка равной длины: шагs=1s=1, с нулями на обоих концахp=(m1)/2p=(m-1)/2, выходная длина после свертки равнаnn.

Первая последовательность ввода-выводаnnКусокembeddingназывается первымnnлексическая позиция, то тогдаsize=nСмысл свертки равной длины, порожденной ядром свертки, состоит в объединении каждой лексемы входной последовательности и ее левого и правогоn12\frac{n-1}{2}Контекстная информация каждого слова сжата в лексему.embedding, что приводит к более высокому уровню и более точной семантике каждой лексемы, модифицированной контекстной информацией. хочу преодолетьTextCNNНедостаток съемки в дальнем режиме, очевидно, заключается в использовании глубокойCNN.

Прямой стек свертки равной длиныСвертка равной длины заставит каждую лексему содержать все больше и больше и более длинную контекстную информацию.Этот метод сделает количество слоев сети очень-очень-очень глубоким, но этот метод слишком громоздкий. Однако, поскольку стек свертки равной длины, свертка равной длины сделает каждую лексемуembeddingСемантическое описание богаче и точнее, а два слоя можно соответствующим образом сложить для улучшения лексемы.embeddingбогатство представлений.

image.png

фиксированныйfeature mapколичество

После выражения семантики каждой лексемы многие соседние слова или смежныеngramЗначение слова можно комбинировать, например, «не надо» и «слишком хорошо» в «Сяомин люди не слишком хороши». Хотя семантика изначально очень далека, когда они появляются как соседнее слово «не слишком хорошо», их семантика в основном эквивалентна «Очень хорошо», можно комбинировать семантику «нельзя» и «слишком хорошо». В то же время процесс слияния может быть полностьюembedding spaceВ исходном тексте очень даже можно напрямую слить «не слишком хорошо» в «очень хорошо», и совершенно незачем сдвигать все смысловое пространство.

На самом деле, по сравнению с очевидным иерархическим различием признаков в изображениях от признаков низкого уровня, таких как «точки, линии, дуги», до признаков высокого уровня, таких как «глаза, нос, рот», признаки в тексте более продвинуты. порядок явно гораздо более плоский, т.е. от слова (1gram) к фразе к3gram,4gramНа самом деле он в значительной степени удовлетворяет характеристикам «семантической замены». Такого рода явление «семантической замены» трудно встретить в изображениях. следовательно,DPCNNиResNetБольшая разница в том, чтосуществуетDPCNNфиксированный мертвыйfeature mapколичество, то есть фиксированныйembedding spaceРазмерность (для простоты понимания в дальнейшем именуемая семантическим пространством), позволяющая сети сделать все соседние слова (соседние слова)ngram) выполняется в исходном пространстве или пространстве, аналогичном исходному пространству (конечно, будет ли это делать сеть на практике, не обязательно, а лишь обеспечивает такое условие). То есть, хотя вся сеть имеет глубокую форму, она может быть совершенно плоской с точки зрения семантического пространства. иResNetЭто постоянное изменение семантического пространства, чтобы семантика изображения продолжала прыгать в семантическое пространство более высокого уровня по мере углубления сетевого слоя.

объединение

Каждый раз, когда вы проходитеsize=3,stride=2size=3,stride=2Слой пула (называемый1/21/2объединяющий слой), длина последовательности сжимается до половины исходной. Это тоже самоеsize=3size=3Ядро свертки , каждый раз после1/21/2После слоя объединения воспринимаемые текстовые фрагменты в два раза длиннее, чем раньше. Например, раньше это было заметно только33информация о длине лексемы, после1/21/2После того, как объединяющий слой сможет воспринимать66информацию о длине лексемы, затем поместите1/21/2объединяющий слой иsize=3Сверточные слои объединяются, как показано на рисунке:

image.png

остаточное соединение

на глубине инициализацииCNNПри часто веса каждого слоя инициализируются малым значением, что приводит к тому, что в исходной сети вход почти каждого последующего слоя близок к00В это время выход сети, естественно, не имеет смысла, и эти малые веса также препятствуют распространению градиента, так что начальная фаза обучения сети часто начинается долго. В то же время, даже если сеть запущена, из-за приближенного непрерывного умножения аффинной матрицы в глубокой сети сеть очень подвержена проблемам градиентного взрыва или дисперсии в процессе обучения (хотя из-за неразделяемых весов , глубинаCNNкоэффициент сетиRNNлучшая сеть). для глубиныCNNПроблема градиентной диффузии для сетейResNetпредложено вshortcut-connection\skip-connection\residual-connection(остаточные соединения) — очень простое, разумное и эффективное решение.

image.png

основной код

class DPCNN(nn.Module):
    def __init__(self, config, word_embedding, freeze):
        """
        config.n_filters: 卷积核个数(对应2维卷积的通道数)
        config.class_num: 类别数
        word_embedding: 训练好的词向量(一个类,包含idx2str、str2idx、vectors)
        freeze: 是否冻结词向量
        """
        super(DPCNN, self).__init__()
        word_embedding = word_embedding
        self.embedding_size = len(word_embedding.vectors[0])
        self.embedding = nn.Embedding.from_pretrained(torch.from_numpy(np.asarray(word_embedding.vectors)),
                                                      freeze=freeze)

        self.conv_region = nn.Conv2d(1, config.n_filters, (3, self.embedding_size), stride=1)
        self.conv = nn.Conv2d(config.n_filters, config.n_filters, (3, 1), stride=1)

        self.max_pool = nn.MaxPool2d(kernel_size=(3, 1), stride=2)
        self.padding1 = nn.ZeroPad2d((0, 0, 1, 1))  # top bottom
        self.padding2 = nn.ZeroPad2d((0, 0, 0, 1))  # bottom

        self.relu = nn.ReLU()
        self.fc = nn.Linear(config.n_filters, config.class_num)

    def forward(self, input_ids):
        # [batch size, seq len, emb dim]
        x = self.embedding(input_ids) 
        # [batch size, 1, seq len, emb dim]
        x = x.unsqueeze(1).to(torch.float32) 
        # [batch size, n_filters, seq len-3+1, 1]
        x = self.conv_region(x)  
        # [batch size, n_filters, seq len, 1]
        x = self.padding1(x)  
        x = self.relu(x)
        # [batch size, n_filters, seq len-3+1, 1]
        x = self.conv(x) 
        # [batch size, n_filters, seq len, 1]
        x = self.padding1(x)  
        x = self.relu(x)
        # [batch size, n_filters, seq len-3+1, 1]
        x = self.conv(x)
        # [batch size, n_filters, 1, 1]
        while x.size()[2] >= 2:
            x = self._block(x)  
        # [batch size, n_filters]
        x_embedding = x.squeeze()  
        # [batch_size, 1]
        x = self.fc(x_embedding)  
        return x

    def _block(self, x):
        x = self.padding2(x)
        px = self.max_pool(x)

        x = self.padding1(px)
        x = F.relu(x)
        x = self.conv(x)

        x = self.padding1(x)
        x = F.relu(x)
        x = self.conv(x)

        # Short Cut
        x = x + px
        return x

TextRNN

RNN

Рекуррентная нейронная сеть (Recurrent Neural Network,RNN) это класс с кратковременной памятью силовая нейронная сеть. В рекуррентной нейронной сети нейроны могут не только получать информацию от других нейронов, но и Он может принимать свою собственную информацию и формировать сетевую структуру с петлями.

image.png

Ht=Tanh([Ht1,Xt]W+b)H_t = Tanh([H_{t-1}, X_t]W+b)

долгосрочные зависимости

Хотя простая рекуррентная сеть теоретически может устанавливать зависимости между состояниями через большие промежутки времени, Именно из-за проблемы взрывающегося или исчезающего градиента фактически можно изучить только краткосрочные зависимости. Таким образом, если моментttВыводyty_tзависит от моментаkkвводxkx_k, когда интервалtkт-кКогда она относительно велика, простой нейронной сети сложно моделировать такие дальнодействующие зависимости, что называется проблемой дальнодействующих зависимостей (Long-Term Dependencies Problem).

RNN

LSTM

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

долговременная память (Long Short-Term Memory,LSTM) сети [Gers et al; Hochreiter et al., 2000; 1997] — это вариант рекуррентных нейронных сетей, который может эффективно решать проблему взрывающегося или исчезающего градиента простых рекуррентных нейронных сетей.

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

image.png

LSTMВ сети представлен механизм ворот (Gating Mechanism) для контроля пути передачи информации.LSTM«Гейт» в сети — это своего рода «мягкий» гейт, значение которого в(0,1)(0, 1)Между ними это означает, что информация передается в определенном соотношении.

Забытые ворота

Забытые воротаftf_tКонтролируйте внутреннее состояние предыдущего моментаct1с_{т-1}Сколько информации нужно забыть.

image.png

входные ворота

Входной вентиль определяет, сколько новой информации добавляется к текущей.CtC_tВойдите. Для этого необходимо выполнить два шага:

  1. Сначала вычислите входной вентильiti-tи возможные состояния ячейки памяти.

image.png

  1. Объедините ворота забвенияftf_tи входные воротаiti_tобновить блок памятиCtC_t

image.png

выходные ворота

В конечном итоге нам нужно определить, какое значение выводить, которое будет основано на состоянии наших ячеек, а также на отфильтрованной версии. Сначала мы проходимsigmoidслой, чтобы определить, какие части состояния ячейки будут выводиться. Далее мы передаем состояние ячейки черезtanhпроцесс (получить[1,1][-1,1]значение) и это иsigmoidВыходы вентилей перемножаются.

image.png

GRU

существуетLSTMВведены три функции ворот: входные ворота, забывающие ворота и выходные ворота для управления передачей информации.Поскольку входные ворота и ворота забывания дополняют друг друга, они имеют определенную степень избыточности.GRUИнтернет напрямую использовать гейт для управления балансом между вводом и забыванием, вGRUВ модели есть только два шлюза: шлюз обновления и шлюз сброса.

на картинкеztz_tиrtr_tпредставляют шлюз обновления и шлюз сброса соответственно. Шлюз обновления используется для управления степенью, в которой информация о состоянии предыдущего момента приводится в текущее состояние.Чем больше значение шлюза обновления, тем больше информации о состоянии предыдущего момента вводится. Ворота сброса контролируют, сколько информации из предыдущего состояния записывается в текущее состояние-кандидат.h~t\tilde{h}_{t}С другой стороны, чем меньше вентиль сброса, тем меньше информации из предыдущего состояния записывается.

image.png

LSTMиGRUВсе важные функции сохраняются благодаря различным функциям ворот, что гарантирует, что вlong-termОн не теряется при распространении. такжеGRUотносительноLSTMОдной вентильной функцией меньше, поэтому количество параметров также меньше, чемLSTM, так в целомGRUобучение проходит быстрее, чемLSTMиз.

Attention

image.png

Attentionрассчитать:

M=tanh(H)M=tanh(H)

α=softmax(uTM)\alpha = softmax(u^TM)

rep=HαTrep = H \alpha_{T}

вHHскрытое состояние, полученное для каждого момента,uuзаcontext vector, случайным образом инициализируемым и обновляемым с помощью обучения, и, наконец, получаем представление предложенияreprep, а затем классифицировать.

основной код

class RNNAttention(nn.Module):
    def __init__(self, config, word_embedding, freeze, batch_first=True):
        """
        config.class_num: 类别数
        config.hidden_dim: rnn隐藏层的维度
        config.n_layers: rnn层数
        config.rnn_type: rnn类型,包括['lstm', 'gru', 'rnn']
        config.bidirectional: 是否双向
        config.dropout: dropout率
        word_embedding: 训练好的词向量(一个类,包含idx2str、str2idx、vectors)
        freeze: 是否冻结词向量
        batch_first: 第一个维度是否是批量大小
        """
        super(RNNAttention, self).__init__()
        word_embedding = word_embedding
        self.embedding_size = len(word_embedding.vectors[0])
        self.embedding = nn.Embedding.from_pretrained(torch.from_numpy(np.asarray(word_embedding.vectors)),
                                                      freeze=freeze)

        if config.rnn_type == 'lstm':
            self.rnn = nn.LSTM(self.embedding_size,
                               config.hidden_dim,
                               num_layers=config.n_layers,
                               bidirectional=config.bidirectional,
                               batch_first=batch_first,
                               dropout=config.dropout)
        elif config.rnn_type == 'gru':
            self.rnn = nn.GRU(self.embedding_size,
                              hidden_size=config.hidden_dim,
                              num_layers=config.n_layers,
                              bidirectional=config.bidirectional,
                              batch_first=batch_first,
                              dropout=config.dropout)
        else:
            self.rnn = nn.RNN(self.embedding_size,
                              hidden_size=config.hidden_dim,
                              num_layers=config.n_layers,
                              bidirectional=config.bidirectional,
                              batch_first=batch_first,
                              dropout=config.dropout)
        # query向量   
        self.u = nn.Parameter(torch.randn(config.hidden_dim * 2), requires_grad=True)
        self.tanh = nn.Tanh()
        self.fc = nn.Linear(config.hidden_dim * 2, config.class_num)

        self.dropout = nn.Dropout(config.dropout)
        self.batch_first = batch_first

    def forward(self, text, text_lengths):
        # 按照句子长度从大到小排序
        text, sorted_seq_lengths, desorted_indices = self.prepare_pack_padded_sequence(text, text_lengths)
        # text = [batch size, sent len]
        embedded = self.dropout(self.embedding(text)).float()
        # embedded = [batch size, sent len, emb dim]

        # pack sequence
        packed_embedded = nn.utils.rnn.pack_padded_sequence(embedded, sorted_seq_lengths, batch_first=self.batch_first)
        self.rnn.flatten_parameters()
        if config.rnn_type in ['rnn', 'gru']:
            packed_output, hidden = self.rnn(packed_embedded)
        else:
            # output (batch, seq_len, num_directions * hidden_dim)
            # hidden (batch, num_layers * num_directions, hidden_dim)
            packed_output, (hidden, cell) = self.rnn(packed_embedded)

        # unpack sequence
        output, output_lengths = nn.utils.rnn.pad_packed_sequence(packed_output, batch_first=self.batch_first)
        # 把句子序列再调整成输入时的顺序
        output = output[desorted_indices]
        # output = [batch_size, seq_len, hidden_dim * num_directionns ]

        alpha = F.softmax(torch.matmul(self.tanh(output), self.u), dim=1).unsqueeze(-1) 
        # alpha = [batch_size, seq_len, 1]
        output_attention = output * alpha # [batch_size, seq_len, hidden_dim * num_directionns ]


        output_attention = torch.sum(output_attention, dim=1)  # [batch_size, hidden_dim]

        fc_input = self.dropout(output_attention)
        out = self.fc(fc_input)
        return out, fc_input

    def prepare_pack_padded_sequence(self, inputs_words, seq_lengths, descending=True):
        """
        for rnn model :按照句子长度从大到小排序
        
        """
        sorted_seq_lengths, indices = torch.sort(seq_lengths, descending=descending)
        _, desorted_indices = torch.sort(indices, descending=False)
        sorted_inputs_words = inputs_words[indices]
        return sorted_inputs_words, sorted_seq_lengths, desorted_indices

TextRCNN

бумага:Войдите на .ACM.org/do i/10.5555…
Код:GitHub.com/649453932/C…

RNNиCNNКак основные архитектуры моделей для задач классификации текста, все они имеют свои преимущества и ограничения.RNNХорошо справляется со структурой последовательности, может учитывать контекстную информацию предложений, ноRNNпринадлежатьbiased model, последние слова в предложении более важны, что может повлиять на окончательный результат классификации, потому что слова, оказывающие наибольшее влияние на классификацию предложения, могут располагаться в любом месте предложения.CNNОн принадлежит к беспристрастной модели и может получить наиболее важные функции за счет максимального объединения, ноCNNРазмер скользящего окна определить непросто.Если выбор слишком мал, легко потерять важную информацию.Если выбор слишком велик, это приведет к огромному пространству параметров. Чтобы устранить ограничения обоих, в этой статье предлагается новая сетевая архитектура, которая использует двунаправленную рекуррентную структуру для получения контекстной информации, которая может уменьшить шум больше, чем традиционные нейронные сети на основе окна, и может быть больше при изучении текстовых представлений.Зарезервированный порядок слов для диапазонов. Во-вторых, используйте слой максимального объединения, чтобы получить важные части текста и автоматически определить, какой признак играет более важную роль в процессе классификации текста.

image.png

обучение представлению слов

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

cl(wi)=f(W(l)cl(wi1)+W(sl)e(wi1))cr(wi)=f(W(r)cr(wi+1)+W(sr)e(wi+1))\begin{array}{l} c_{l}\left(w_{i}\right)=f\left(W^{(l)} c_{l}\left(w_{i-1}\right)+W^{(s l)} e\left(w_{i-1}\right)\right) \\ c_{r}\left(w_{i}\right)=f\left(W^{(r)} c_{r}\left(w_{i+1}\right)+W^{(s r)} e\left(w_{i+1}\right)\right) \end{array}

в,cl(wi)c_l(w_i)репрезентативное словоwiw_iлевый контекст ,cl(wi)c_l(w_i)левый контекст по предыдущему словуcl(wi1)c_l(w_{i-1})и вектор вложения слова предыдущего словаe(wi1)e(w_{i-1})Вычисленный, как показано в формуле (1), левый контекст первого слова всех предложений использует одни и те же общие параметрыcl(w1)c_l(w_1).W(l),W(sl)W^{(l)},W^{(sl)}Используется для объединения семантики левого контекста предыдущего слова и семантики предыдущего слова со словом.wiw_iв левом контекстном представлении . Обработка правого контекста точно такая же, как и левого контекста, и те же общие параметры используются для правого контекста последнего слова всех предложений.cr(wn)c_r(w_n). После получения левого и правого контекстных представлений каждого слова в предложении слово может быть определено.wiw_iвыражается следующим образом

xi=[cl(wi);e(wi);cr(wi)]\boldsymbol{x}_{i}=\left[\boldsymbol{c}_{l}\left(w_{i}\right) ; \boldsymbol{e}\left(w_{i}\right) ; \boldsymbol{c}_{r}\left(w_{i}\right)\right]

на самом деле словаwiw_i, вектор представления слова, встраивающий словоe(wi)e(w_i)и правильный вектор контекста словаce(wi)c_e(w_i)склеенный результат. получитьwiw_iпредставительствоxix_iПосле этого вы можете войти в функцию активации, чтобы получитьwiw_iскрытый семантический векторyi(2)y_i^{(2)}.

yi(2)=tanh(W(2)xi+b(2))\boldsymbol{y}_{i}^{(2)}=\tanh \left(W^{(2)} \boldsymbol{x}_{i}+\boldsymbol{b}^{(2)}\right)

Изучение текстового представления

После прохождения сверточных слоев получаются представления всех слов, и они сначала подвергаются операции max-pooling, которая может помочь найти в предложении наиболее важную скрытую семантическую информацию.

y(3)=maxi=1nyi(2)\boldsymbol{y}^{(3)}=\max _{i=1}^{n} \boldsymbol{y}_{i}^{(2)}

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

y(4)=W(4)y(3)+b(4)pi=exp(yi(4))k=1nexp(yk(4))\begin{aligned} &\boldsymbol{y}^{(4)}=W^{(4)} \boldsymbol{y}^{(3)}+\boldsymbol{b}^{(4)}\\ &p_{i}=\frac{\exp \left(\boldsymbol{y}_{i}^{(4)}\right)}{\sum_{k=1}^{n} \exp \left(\boldsymbol{y}_{k}^{(4)}\right)} \end{aligned}

основной код

class RCNN(nn.Module):
    def __init__(self, config, word_embedding, freeze, batch_first=True):
        """
        config.class_num: 类别数
        config.hidden_dim: rnn隐藏层的维度
        config.n_layers: rnn层数
        config.rnn_type: rnn类型,包括['lstm', 'gru', 'rnn']
        config.bidirectional: 是否双向
        config.dropout: dropout率
        word_embedding: 训练好的词向量(一个类,包含idx2str、str2idx、vectors)
        freeze: 是否冻结词向量
        batch_first: 第一个维度是否是批量大小
        """
        super(RCNN, self).__init__()
        word_embedding = word_embedding
        self.embedding_size = len(word_embedding.vectors[0])
        self.embedding = nn.Embedding.from_pretrained(torch.from_numpy(np.asarray(word_embedding.vectors)),
                                                      freeze=freeze)

        if config.rnn_type == 'lstm':
            self.rnn = nn.LSTM(self.embedding_size,
                               config.hidden_dim,
                               num_layers=config.n_layers,
                               bidirectional=config.bidirectional,
                               batch_first=batch_first,
                               dropout=config.dropout)
        elif config.rnn_type == 'gru':
            self.rnn = nn.GRU(self.embedding_size,
                              hidden_size=config.hidden_dim,
                              num_layers=config.n_layers,
                              bidirectional=config.bidirectional,
                              batch_first=batch_first,
                              dropout=config.dropout)
        else:
            self.rnn = nn.RNN(self.embedding_size,
                              hidden_size=config.hidden_dim,
                              num_layers=config.n_layers,
                              bidirectional=config.bidirectional,
                              batch_first=batch_first,
                              dropout=config.dropout)
        
        # 1 x 1 卷积等价于全连接层,故此处使用全连接层代替
        self.fc_cat = nn.Linear(config.hidden_dim * 2 + self.embedding_size, self.embedding_size)
        self.fc = nn.Linear(self.embedding_size, config.class_num)

        self.dropout = nn.Dropout(config.dropout)
        self.batch_first = batch_first

    def forward(self, text, text_lengths):
        # 按照句子长度从大到小排序
        text, sorted_seq_lengths, desorted_indices = self.prepare_pack_padded_sequence(text, text_lengths)
        # text = [batch size, sent len]
        embedded = self.dropout(self.embedding(text)).float()
        # embedded = [batch size, sent len, emb dim]

        # pack sequence
        packed_embedded = nn.utils.rnn.pack_padded_sequence(embedded, sorted_seq_lengths, batch_first=self.batch_first)
        self.rnn.flatten_parameters()
        if config.rnn_type in ['rnn', 'gru']:
            packed_output, hidden = self.rnn(packed_embedded)
        else:
            # output (batch, seq_len, num_directions * hidden_dim)
            # hidden (batch, num_layers * num_directions, hidden_dim)
            packed_output, (hidden, cell) = self.rnn(packed_embedded)

        # unpack sequence
        output, output_lengths = nn.utils.rnn.pad_packed_sequence(packed_output, batch_first=self.batch_first)
        # 把句子序列再调整成输入时的顺序
        output = output[desorted_indices]
        # output = [batch_size, seq_len, hidden_dim * num_directionns ]

        batch_size, max_seq_len, hidden_dim = output.shape

        # 拼接左右上下文信息
        output = torch.tanh(self.fc_cat(torch.cat((output, embedded), dim=2)))
        # output = [batch_size, seq_len, embedding_size]

        output = torch.transpose(output, 1, 2)
        output = F.max_pool1d(output, int(max_seq_len)).squeeze().contiguous()

        return self.fc(output)

    def prepare_pack_padded_sequence(self, inputs_words, seq_lengths, descending=True):
        """
        for rnn model :按照句子长度从大到小排序
        
        """
        sorted_seq_lengths, indices = torch.sort(seq_lengths, descending=descending)
        _, desorted_indices = torch.sort(indices, descending=False)
        sorted_inputs_words = inputs_words[indices]
        return sorted_inputs_words, sorted_seq_lengths, desorted_indices

HAN

бумага:Woohoo.ACL Web.org/anthology/N…
Код:GitHub.com/daily/special…

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

image.png

Вся структура сети включает пять частей:

  1. кодировщик последовательности слов

  2. слой внимания на уровне слов

  3. Кодировщик предложений

  4. Слой внимания на уровне предложения

  5. Классификация

Вся сетевая структура состоит из двустороннихGRUМеханизм сети и внимания объединен.

кодировщик порядка слов

дано слово в предложенииwitw_{it}iiозначает первыйiiприговор,ttозначает первыйttслова. Через матрицу встраивания словWeW_eПреобразуйте слова в векторные представления следующим образом:

xit=Wewitx_{it}=W_e w_{it}

Введите полученный вектор слов в кодировщик слов, т.е.GRU, два направленияGRUВыходы объединяются вместе для получения скрытых векторов на уровне слов.hh

внимание на уровне слов

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

uit=tanh(Wwhit+bw) u_{i t} =\tanh \left(W_{w} h_{i t}+b_{w}\right)

αit=exp(uituw)texp(uituw)\alpha_{i t} =\frac{\exp \left(u_{i t}^{\top} u_{w}\right)}{\sum_{t} \exp \left(u_{i t}^{\top} u_{w}\right)}

si=tαithits_{i} =\sum_{t} \alpha_{i t} h_{i t}

В приведенной выше формулеuitu_{it}даhith_{it}Представление скрытого слоя ,aita_{it}является поsoftmaxНормализованный весовой коэффициент после обработки функции,uwu_wпредставляет собой случайно инициализированный вектор, который затем обучается как параметр модели,sis_iэто то, что мы получаемiiВекторное представление предложения.

Кодировщики предложений и внимание на уровне предложений

Для векторов уровня предложения мы используем аналогичныйGRUи слой внимания, и, наконец, взвешенное суммирование скрытых векторных представлений всех предложений в документе для получения вектора документа всего документа.vv, вектор классифицируется полносвязным классификатором.

основной код

class HierAttNet(nn.Module):
    def __init__(self, rnn_type, word_hidden_size, sent_hidden_size, num_classes, word_embedding, 
                       n_layers, bidirectional, batch_first, freeze, dropout):
        super(HierAttNet, self).__init__()
        self.word_embedding = word_embedding
        self.word_hidden_size = word_hidden_size
        self.sent_hidden_size = sent_hidden_size
        self.word_att_net = WordAttNet(rnn_type,word_embedding, word_hidden_size,n_layers,bidirectional,batch_first,dropout,freeze)
        self.sent_att_net = SentAttNet(rnn_type,sent_hidden_size, word_hidden_size,n_layers,bidirectional,batch_first,dropout, num_classes)


    def forward(self, batch_doc, text_lengths):
        output_list = []
        # ############################ 词级 #########################################
        for idx,doc in enumerate(batch_doc):
            # 把一篇文档拆成多个句子
            doc = doc[:text_lengths[idx]]
            doc_list = doc.cpu().numpy().tolist()
            sep_index = [i for i, num in enumerate(doc_list) if num == self.word_embedding.stoi['[SEP]']]
            sentence_list = []
            if sep_index:
                pre = 0
                for cur in sep_index:
                    sentence_list.append(doc_list[pre:cur])
                    pre = cur

                sentence_list.append(doc_list[cur:])

            else:
                sentence_list.append(doc_list)
            max_sentence_len = len(max(sentence_list,key=lambda x:len(x)))
            seq_lens = []
            input_token_ids = []
            for sent in sentence_list:
                cur_sent_len = len(sent)
                seq_lens.append(cur_sent_len)
                input_token_ids.append(sent+[self.word_embedding.stoi['PAD']]*(max_sentence_len-cur_sent_len))
            input_token_ids = torch.LongTensor(np.array(input_token_ids)).to(batch_doc.device)
            seq_lens = torch.LongTensor(np.array(seq_lens)).to(batch_doc.device)
            word_output, hidden = self.word_att_net(input_token_ids,seq_lens)
            # word_output = [bs,hidden_size]
            output_list.append(word_output)

        max_doc_sent_num = len(max(output_list,key=lambda x: len(x)))
        batch_sent_lens = []
        batch_sent_inputs = []
        
        # ############################ 句子级 #########################################
        for doc in output_list:
            cur_doc_sent_len = len(doc)
            batch_sent_lens.append(cur_doc_sent_len)
            expand_doc = torch.cat([doc,torch.zeros(size=((max_doc_sent_num-cur_doc_sent_len),len(doc[0]))).to(doc.device)],dim=0)
            batch_sent_inputs.append(expand_doc.unsqueeze(dim=0))

        batch_sent_inputs = torch.cat(batch_sent_inputs, 0)
        batch_sent_lens = torch.LongTensor(np.array(batch_sent_lens)).to(doc.device)
        output = self.sent_att_net(batch_sent_inputs,batch_sent_lens)
        return output

BERT

BERT(Bidirectional Encoder Representations from Transformers)РелизNLPЭто событие стало одной из последних вех в развитии отрасли.NLPНачало новой эры.BERTМодель побила несколько рекордов для задач, связанных с обработкой языка. существуетBERTВскоре после публикации статьи команда также обнародовала код моделей и сделала доступными для загрузки модели, которые были предварительно обучены на крупномасштабных наборах данных. Это важное событие, поскольку оно позволяет любому, кто создает модель машинного обучения для обработки языка, использовать эту мощную функцию в качестве готового компонента, избавляя от необходимости обучать модель обработки языка с нуля, время, энергию, знания и ресурсы.

Подробнее см.【Графический BERT】,[Иллюстрированная модель BERT]

Task 1: Masked Language Model

так какBERTПредсказывать информацию центрального слова необходимо через контекстную информацию, и в то же время не ожидается, что модель увидит информацию центрального слова заранее, поэтому предлагается новый метод.Masked Language ModelМетод предварительного обучения , то есть случайное предсказание по входным даннымmaskОтбросьте несколько слов, а затем предскажите слово по контексту, как в задаче на закрытие.

В предтренировочном задании 15%Word PieceБудетmask, 15%Word Piece, в 80% случаев он будет напрямую заменен на[Mask], замените его любым другим словом в 10% случаев и сохраните исходное слово в 10% случаевToken

  • нет 100%maskпричина
    • если одно из предложенийToken100% будетmaskвыкл, затем вfine-tuningКогда в модели появятся невидимые слова
  • Присоединяйтесь к 10% случайнымtokenпричина
    • TransformerСохранить для каждого входаtokenраспределенное представление , иначе модель запомнила бы это[mask]является конкретнымtoken
    • Кроме того, кодер не знает, какие слова нужно предсказать, а какие нет, поэтому он вынужден запоминать каждое.tokenвектор представления
  • Кроме того, каждыйbatchsizeТолько 15% слов былиmaskПричина в том, что из-за снижения производительности двунаправленный кодировщик медленнее обучается, чем кодировщик с одним элементом.

Task 2: Next Sequence Prediction

только одинMLMзадача не достаточно, чтобы сделатьBERTДля решения задач на оценку отношения предложений, таких как понимание прочитанного, добавляется дополнительная предтренировочная задача, а именноNext Sequence Prediction.

Конкретная задача представляет собой задачу суждения об отношениях между предложениями, то есть определить, является ли предложение B следующим предложением предложения A, и если да, то вывести результат.IsNext, иначе выводNotNext.

Обучающие данные генерируются путем случайного извлечения двух последовательных предложений из параллельного корпуса, 50% которых зарезервированы для двух извлеченных предложений, которые удовлетворяют требованиям.IsNextотношения, остальные 50% вторых предложений случайным образом взяты из ожиданий, и их отношенияNotNextиз. Это отношение хранится в[CLS]символ

входить

  • Token Embeddings: Традиционный векторный слой слов, первый символ каждого входного образца должен быть установлен на[CLS], который можно использовать для последующих задач классификации.Если есть два разных предложения, нужно использовать[SEP]разделены, и последний символ должен быть[SEP]указывает на прекращение
  • Segment Embeddings:за[0,1]последовательность, используемая вNSPРазличать два предложения в задании, что удобно для задания на оценку отношений между предложениями.
  • Position EmbeddingsTransformerВекторы положения в различны,BERTВектор положения в непосредственно обучается

Fine-tunninng

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

  • Для задач классификации одного предложения, таких как анализ настроений, вы можете напрямую ввести одно предложение (нет необходимости[SEP]отдельные двойные предложения),[CLS]Вывод напрямую вводится в классификатор для классификации
  • Для задачи на пару предложений (задача оценки отношения предложений) вам необходимо использовать[SEP]разделить два предложения на модель, а затем снова нужно только[CLS]Результат отправляется в классификатор для классификации
  • Для заданий на ответы на вопросы вопрос и ответ объединяются вBERTВ модели выходной вектор позиции ответа затем бинаризуется и обрабатывается в направлении предложения.softmax(просто предскажите начальную и конечную позиции)
  • Для задач распознавания именованных сущностей достаточно классифицировать выходные данные каждой позиции.Если выходные данные каждой позицииCRFдобьется лучших результатов.

недостаток

  • BERTпредтренировочные задачиMLMОн позволяет кодировать последовательность с помощью контекста, но в то же время делает так, что данные в процессе предобучения не совпадают с данными в тонкой настройке, что затрудняет адаптацию к генеративным задачам.
  • Кроме того, BERT не учитывает прогнозы.[MASK]Корреляция между является необъективной оценкой совместной вероятности языковой модели
  • Из-за ограничения максимальной длины ввода подходит для задач на уровне предложения и абзаца, но не подходит для задач на уровне документа (например, для классификации длинного текста).
  • Подходит для решения задач естественного семантического понимания (NLU), не подходит для задач генерации естественного языка (NLG)

BERTОптимизацию классификации можно попробовать:

  • Попробуйте разные предварительно обученные модели, такие какRoBERT,WWM,ALBERT

  • Кроме[CLS]также можно использоватьavg,maxОбъединение используется для представления предложений, и даже разные слои могут быть объединены.

Инкрементная предварительная подготовка данных домена

  • Интегрируйте дистилляцию, обучите несколько больших моделей и объедините их в одну

  • Сначала используйте многозадачное обучение, а затем переходите к своим собственным задачам

основной код

class BertForSeqCLS(nn.Module):
    def __init__(self, config, train=True):
        super(BertForSeqCLS, self).__init__()
        self.bert = BertModel.from_pretrained(config.bert_path)
        # 对bert进行训练
        for param in self.bert.parameters():
            param.requires_grad = train
        
        self.dropout = nn.Dropout(config.dropout)
        self.fc = nn.Linear(768 * 3, config.class_num)

    def forward(self, input_ids, attention_mask, labels=None):
        # input_ids  输入的句子序列
        # seq_len  句子长度
        # attention_masks  对padding部分进行mask,和句子一个size,padding部分用0表示,如:[1, 1, 1, 1, 0, 0]
        # pooled_out [batch_size, 768]
        # sentence [batch size, sen len,  768]
        
        outputs = self.bert(input_ids, attention_mask=attention_mask, 
                                                output_hidden_states=True)
        cat_out = torch.cat((outputs.pooler_output, outputs.hidden_states[-1][:,0], 
                             outputs.hidden_states[-2][:, 0]), 1)
        logits = self.fc(self.dropout(cat_out))

        loss = None
        if labels is not None:
            loss = F.cross_entropy(logits.view(-1, config.class_num), labels.view(-1))

        return {"loss": loss, "logits": logits}

Советы по классификации текста

image.png

построение набора данных

Во-первых, построение системы меток.Когда вы получаете задание, вы сначала пробуете одну или две сотни меток, чтобы увидеть, сколько из них трудно определить (подумайте больше, чем 1 с).Если пропорция слишком велика, то определение этой задачи будет проблематичным. Может случиться так, что система маркировки неясна или категории, которые нужно классифицировать, слишком сложны.В это время следует попросить владельца проекта дать отзыв, а не продолжать.

Во-вторых, построение обучающего оценочного набора.Можно построить два оценочных набора: один представляет собой набор онлайн-оценок, который соответствует распределению реальных данных, чтобы отразить онлайн-эффект, а другой представляет собой набор случайных оценок, который равномерно отбирается после дедупликация с помощью правил, соответствующих модели. Распределение обучающего набора максимально соответствует оценочному набору.Иногда мы будем переходить в аналогичное поле, чтобы получить готовые помеченные обучающие данные.В это время мы должны обратить внимание на настройку распределения, например длину предложения , пунктуация, чистота и т. д., и изо всех сил стараемся быть собой. Невозможно сказать, взято ли это предложение из этой задачи или заимствовано у кого-то другого.

Наконец, очистка данных:

  • Убрать сильные текстовые шаблоны:Например, в классификации новостных тем бесполезны отчеты ХХ и редактирование ХХ высокочастотных полей в каких-то облазивших данных.Можно посчитать фрагменты или слова корпуса и убрать бесполезное элементы с высокой частотой. Есть также некоторые, которые, очевидно, повлияют на оценку модели.Например, при оценке того, является ли предложение бессмысленной болтовней, обнаруживается, что добавление точки превратит выборку из положительной в отрицательную, потому что небольшая болтовня, ожидаемая обучение редко имеет период (у всех привычка печатать), поэтому удаление этого шаблона намного лучше

  • Исправьте ошибки маркировки: просто соберите обучающий набор и оценочный набор, используйте набор данных для обучения модели в течение двух или трех эпох (для предотвращения переобучения), затем предскажите набор данных, удалите неправильную модель и нажмите abs(label-prob) Сортировка, если будет меньше, то вы сами увидите, а если больше, то скормите маркировщикам.Возможно улучшить качество данных на несколько пунктов.

длинный текст

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

хочу использоватьBERTСамый простой способ — грубо обрезать, например, взять всего несколько слов из начала предложения + конец предложения, начало предложения + сито tfidf; или предсказать каждое предложение и, наконец, обобщить результаты.

Кроме того, есть несколько модифицированных магией моделей, которые вы можете попробовать, напримерBERT+HAN,XLNet,Reformer,Longformer.

прочность

В практических приложениях надежность является очень важным вопросом, иначе перед лицомbadcaseВременами будет очень неловко, как можно так четко разделить, а добавить слово неправильно?

Здесь вы можете напрямую использовать некоторые грубые улучшения данных, добавлять стоп-слова, добавлять знаки препинания, удалять слова, заменять синонимы и т. д. Если эффект уменьшается, стирайте расширенные обучающие данные.

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

Ссылка на ссылку

  1. АР Вест V.org/ABS/1607.01…
  2. АР Вест V.org/ABS/1408.58…
  3. Love.Tencent.com/Arab/Media…
  4. Войдите на .ACM.org/do i/10.5555…
  5. Woohoo.ACL Web.org/anthology/N…
  6. zhuanlan.zhihu.com/p/266364526
  7. cloud.Tencent.com/developer/ ах…
  8. Блог woohoo.cn на.com/sandwich НЛП…
  9. GitHub.com/Джефф 0628…
  10. zhuanlan.zhihu.com/p/349086747
  11. zhuanlan.zhihu.com/p/35457093
  12. Ууху. Deeper.com/article/431…
  13. Ууху. Call.com/question/32…