Sklearn делает несколько изменений в реализации своей версии векторизатора TFIDF, поэтому, чтобы воспроизвести точные результаты, вам нужно добавить следующие вещи в свою пользовательскую реализацию векторизатора tfidf:
- У Sklearn есть свои словарь, сгенерированный из idf sroted в алфавитном порядке
- Sklearn формула idf отличается от стандартной формулы учебника. Здесь константа «1» добавляется в числитель и знаменатель idf, как если бы был замечен дополнительный документ, содержащий каждый термин в коллекции ровно один раз, что предотвращает деление на ноль.
IDF(t)=1+(loge((1 + Total number of documents in collection)/(1+Number of documents with term t in it))
. - Sklearn применяет Нормализация L2 к своей выходной матрице.
- Окончательный вывод векторизатора sklearn tfidf является разреженной матрицей.
Сейчас с учетом следующего корпуса:
corpus = [
'this is the first document',
'this document is the second document',
'and this is the third one',
'is this the first document',
]
Реализация Sklearn:
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
vectorizer.fit(corpus)
skl_output = vectorizer.transform(corpus)
print(vectorizer.get_feature_names())
output : [‘and’, ‘document’, ‘first’, ‘is’, ‘one’, ‘second’, ‘the’, ‘third’, ‘this’]
print(skl_output[0])
Вывод:
(0, 8) 0.38408524091481483
(0, 6) 0.38408524091481483
(0, 3) 0.38408524091481483
(0, 2) 0.5802858236844359
(0, 1) 0.46979138557992045
Мне нужно повторить приведенный выше результат с помощью пользовательской реализации, т.е. записи простой код python.
Я написал следующий код:
from collections import Counter
from tqdm import tqdm
from scipy.sparse import csr_matrix
import math
import operator
from sklearn.preprocessing import normalize
import numpy
# The fit function helps in creating a vocabulary of all the unique words in the corpus
def fit(dataset):
storage_set = set()
if isinstance(dataset,list):
for document in dataset:
for word in document.split(" "):
storage_set.add(word)
storage_set = sorted(list(storage_set))
vocab = {j:i for i,j in enumerate(storage_set)}
return vocab
vocab = fit(corpus)
print(vocab)
output : {‘and’: 0, ‘document’: 1, ‘first’: 2, ‘is’: 3, ‘one’: 4, ‘second’: 5, ‘the’: 6, ‘third’: 7, ‘this’: 8}
This output is matching with the output of the sklearn above
#Returs a sparse matrix of the all non-zero values along with their row and col
def transform(dataset,vocab):
row = []
col = []
values = []
for ibx,document in enumerate(dataset):
word_freq = dict(Counter(document.split()))
for word, freq in word_freq.items():
col_index = vocab.get(word,-1)
if col_index != -1:
if len(word)<2:
continue
col.append(col_index)
row.append(ibx)
td = freq/float(len(document)) # the number of times a word occured in a document
idf_ = 1+math.log((1+len(dataset))/float(1+idf(word)))
values.append((td) * (idf_))
return normalize(csr_matrix( ((values),(row,col)), shape=(len(dataset),len(vocab))),norm='l2' )
print(transform(corpus,vocab))
Вывод:
(0, 1) 0.3989610517704845
(0, 2) 0.602760579899478
(0, 3) 0.3989610517704845
(0, 6) 0.3989610517704845
(0, 8) 0.3989610517704845
Как видите, этот вывод не совпадает с значения из вывода sklearn. Я несколько раз пролистал логи c, везде пробовал отлаживать. Тем не менее, я не смог определить, почему моя пользовательская реализация не соответствует выводу sklearn. Буду признателен за любые идеи.