#python #scikit-learn #nlp #tfidfvectorizer
Вопрос:
Я реализую функцию sklearn (vectorizer.idf_) с нуля и сравниваю результат. Так что для данного корпуса, скажем,
РЕАЛИЗАЦИЯ SKLEARN:-
corpus = [
'this is the first document',
'this document is the second document',
'and this is the third one',
'is this the first document',
]
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(vectorizer.idf_)
OUTPUT:- [1.91629073 1.22314355 1.51082562 1. 1.91629073 1.91629073
1. 1.91629073 1. ]
Мой пользовательский код выглядит следующим образом, где у меня есть метод fit (), который преобразует корпус в словарь, и функция idf (), в которой будет подсчитываться, сколько раз встречается конкретное слово в данном корпусе, и функция transform (), в которой я вычисляю значения idf.
corpus = [
'this is the first document',
'this document is the second document',
'and this is the third one',
'is this the first document',
]
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)}
#Idf_values_of_all_unique_words=IDF(dataset,storage_set)
#print(list(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}
def idf(dataset,word):
count=0
for row in dataset:
if word in row:
count =1
return count
def transform(dataset,vocab):
row = []
col = []
values = []
idf_value=[]
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)
term_freq = freq/(len(document)) # the number of times a word occured in a document
idf_ = 1 math.log((1 len(dataset))/(1 idf(dataset,word)))
values.append((term_freq) * (idf_))
idf_value.append(idf_)
print(idf_value)
OUTPUT:- [1.0, 1.0, 1.0, 1.5108256237659907, 1.2231435513142097, 1.0, 1.2231435513142097, 1.0, 1.0, 1.916290731874155, 1.916290731874155, 1.0, 1.0, 1.0, 1.916290731874155, 1.916290731874155, 1.0, 1.0, 1.0, 1.5108256237659907, 1.2231435513142097]
поэтому, если я сравниваю вывод sklearn для оценки idf, это массив из 9 значений, так как в корпусе 9 разных слов, но я получаю массив размером 22. Может ли кто-нибудь помочь мне понять, где я делаю это неправильно.
Комментарии:
1. используйте
print()
для просмотра значений переменных и того, какие строки кода выполняются, и сравните это с расчетами на бумаге.2. у вас есть вложенные
for
циклы-первые запуски 4 раза (количество документов) , внутренние запуски 9 раз (количество слов), поэтому он может создать даже 36 элементов. Это может означать, что вы неправильно рассчитали его. Вероятно, вы сохраняете каждое значение отдельно — для каждого документа разделенное значение. Но для каждого слова вы должны суммировать значения из всех документов. возможно, вам следует сохранить результат какvalues[word] = [result1, result2]
и позже использовать их все для расчета(term_freq) * (idf_)
Ответ №1:
Для его расчета idf_
требуется всего один цикл.
def transform(dataset, vocab):
idf_value = []
dataset_len = len(dataset)
for word in vocab:
idf_ = 1 math.log( (1 dataset_len) / (1 idf(dataset, word)) )
idf_value.append(idf_)
print(idf_value)
Потому что вы запускаете его в другом цикле, поэтому append()
добавляете одно и то же значение много раз.
Таким образом, вы можете сначала рассчитать idf_
все, чтобы добавить их в список.
Или вы должны сохранить idf_
как словарь word: idf_
, а затем добавить то же самое снова idf_
, это не составит проблемы.
Этот код дает мне те же результаты, что и vectorizer.idf_
Это дает почти тот же результат, skl_output
что и (я должен умножить на 10), но несколько значений отличаются — и я не знаю, почему.
def transform(dataset, vocab):
values = []
idf_value=[]
dataset_len = len(dataset)
for word in vocab:
idf_ = 1 math.log( (1 dataset_len) / (1 idf(dataset, word)) )
idf_value.append(idf_)
print(idf_value)
for doc_idx, document in enumerate(dataset):
document_len = len(document)
word_freq = dict(Counter(document.split()))
for word, freq in word_freq.items():
word_idx = vocab[word]
term_freq = freq/document_len
idf_ = 1 math.log( (1 dataset_len) / (1 idf(dataset, word)) )
values.append( ((doc_idx, word_idx), term_freq * idf_ * 10))
for item in values:
print(item)
Редактировать:
Лучше использовать split(' ')
, когда вы проверяете
if word in row.split(' '):
потому "is" in "this"
что дает True
, но тебе нужно False
и "is" in ["this"]
дает правильно False