Поиск интересных ключевых слов: TF / IDF из Корпорации с использованием Ruby

В наши дни неограниченное количество данных стало нормой. Обращайтесь куда угодно или в любую организацию, и объем собираемых данных растет в геометрической прогрессии, и конца этому не видно. Существует множество инструментов и методов, позволяющих их добыть и найти интересную закономерность. Один из таких способов выявления интересных ключевых слов - использовать TF / IDF.

Из Википедии, определение TF / IDF,

представляет собой числовую статистику, которая предназначена для отражения того, насколько важно слово для документа в коллекции или корпусе. [1] Он часто используется в качестве весового коэффициента при поиске информации, интеллектуальном анализе текста и пользовательском моделировании.

Задача

Наша цель здесь - определить ключевые слова из 1, 2 и 3 слов, которые называются униграммой, биграммой и триграммой соответственно в парадигме НЛП. Прежде чем мы перейдем к коду, нам нужно понять хотя бы одно практическое использование этого, иначе теоретическая часть будет казаться очень сухой.

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

Это хороший кандидат на решение проблемы классификации машинного обучения. Мы можем взять подмножество статей из лота, определить обучающий набор и пометить каждую статью по ее категории, и позволить машине обучиться сама. Однако вместо того, чтобы выгружать содержимое всей статьи на машину, наш набор функций мог бы состоять только из интересных слов. Это не только сэкономит время, но и даст результаты с очень высокой точностью. Именно здесь мы собираемся использовать TF / IDF.

Давайте использовать общие стоп-слова, чтобы отфильтровать весь текст, который можно найти где-нибудь в stanfordnlp.

class TfIdf
  attr_accessor :tf, :idf, :tf_idf
def initialize(corpus=[], options={})
    @stopwords = File.read('stopwords.txt')
    @docs = process_corpus(corpus, options[:ngram], options[:min_length])
    @ngram = options[:ngram]
    @min_length = options[:min_length]
    @tf = []
    @idf = {}
    @tf_idf = []
    @docs_size = @docs.size
    compute_tf_and_idf
    compute_tf_idf(options[:limit])
  end
def ngrams(n, string, min_length) 
    string.downcase.split(/\W+/).reject{|r| (r.length < min_length) || (@stopwords.include?(r)) }.each_cons(n).to_a.map{|c| c.join(" ")}
  end
def process_corpus(corpus, ngram, min_length)
    corpus.map{|c| ngrams(ngram, c, min_length)}
  end
def compute_tf_and_idf
    @docs.each do |words|
      terms_freq_in_words = words.inject(Hash.new(0)) { |h, e| h[e] += 1 ; h }
      @tf.push(terms_freq_in_words)
      distinct_words = words.uniq
      distinct_words.each do |w|
        if ( @idf.has_key?(w) )
          y = @docs_size / ( 10**(@idf[w]) )
          y += 1
          @idf[w] = Math.log10(@docs_size / y)
        else
          @idf[w] = Math.log10(@docs_size)
        end
      end
    end
  end
def compute_tf_idf(limit)
    unless @tf.empty?
      tf_freq = @tf.first #Only calculate TF IDF for the first item
      tfidf = Hash.new(0)
      tf_freq.each do |key,value|
        tfidf[key] = @idf[key] * value
      end
      tfidf = Hash[tfidf.sort_by { |k,v| -v }[0..limit-1]]
      @tf_idf.push(tfidf)
    end
    @tf_idf = @tf_idf.map(&:keys).flatten
  end
end

Проверяю это. Наш тестовый сценарий аналогичен сценарию использования статей. Однако здесь для ясности наша цель - определить жанр фильма по его субтитрам. Давайте возьмем субтитры из 10 разных фильмов и пройдемся по нашей логике TF / IDF, чтобы увидеть интересные термины.

Вот несколько случайных фильмов, которые я выбрал для этого сравнения

  1. Бойцовский клуб
  2. Запах женщины
  3. Экзорцист
  4. xXx
  5. парк Юрского периода
  6. Побег из Шоушенка
  7. Аватар
  8. Развлечение с Диком и Джейн
  9. Призрак писатель
  10. Зарождение

Я скачал субтитры ко всем вышеперечисленным фильмам с http://www.yifysubtitles.com/

Поскольку субтитры в формате .srt, мне пришлось удалить количество строк и продолжительность, а также выполнить некоторую очистку, чтобы извлечь только слова.

require './tf_idf'
movies = Dir["./subs/*.srt"]
movie_sub = {}
movie_freqs = {}
movies.each do |movie|
  movie_name = movie.gsub("./subs/","").gsub(".srt","")
  movie_sub[movie_name] = File.read(movie, encoding: 'iso-8859-1').split("\r\n").reject{|t| t=="" || t.to_i > 0 || t.include?("-->")}.join(' ').gsub("...", "").gsub("<i>", "").gsub("</i>","").gsub(/["!()\[\]]/,"")
  movie_freqs[movie_name] = {1 => [], 2 => [], 3 => []}
end
movie_sub.keys.each do |movie_name|
  temp_sub = movie_sub.clone
  corpora = [temp_sub.delete(movie_name)] + temp_sub.values
  (1...4).each do |ngram|
    @tf_idf = TfIdf.new corpora, limit: 8, ngram: ngram, min_length: 3
    movie_freqs[movie_name][ngram] += @tf_idf.tf_idf
  end
end
movie_freqs.keys.each do |movie_name|
  puts "**********************************"
  puts "Unigrams for #{movie_name}"
  puts "**********************************"
  puts movie_freqs[movie_name][1]
  puts
end

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

Запустив приведенный выше код, вы увидите самые интересные ключевые слова (униграммы) для каждого из фильмов.

Биграммы,

И триграммы,

Откуда отсюда?

Изначально мы начали этот набор для решения проблемы классификации. Мы уже достигли состояния, когда теперь можем находить некоторые интересные слова, уникальные для каждого фильма. В этом примере мы ограничились только 10 ключевыми словами, однако мы можем увеличить этот предел до 100 или даже до 1000 и получить больше фильмов того же жанра и создать уникальное ключевое слово для обучающего набора.

Мы также можем использовать механизм классификации сущностей, такой как идентификатор имени / города, чтобы исключить имена и другие слова, которые могут не очень помочь нам в определении жанра фильма. Теперь, когда мы решили сложную задачу, мы можем перейти к doc2vec или tensorflow, которые подробно описаны здесь.

Francium Tech - технологическая компания, специализирующаяся на поставке высококачественного масштабируемого программного обеспечения на экстремальных скоростях. Числа нас не пугают. Если у вас есть какие-либо требования или вы хотите бесплатно проверить работоспособность архитектуры вашей системы, напишите письмо по адресу [email protected], мы свяжемся с вами!

ariadne
gunfire