【问题标题】:Build (vectorizer.idf_) function of Sklearn with from Scratch使用 from Scratch 构建 Sklearn 的 (vectorizer.idf_) 函数
【发布时间】:2021-09-02 16:13:11
【问题描述】:

我正在从头开始实现 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() 的函数,它将计算特定单词在给定语料库中出现的次数和变换() 函数,我正在计算 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 的数组。有人可以帮我理解我在做什么错了。

【问题讨论】:

  • 使用 print() 查看变量中的值以及执行了哪些代码行 - 并将其与纸上的计算进行比较。
  • 你已经嵌套了 for-loops - 第一次运行 4 次(文档数),内部运行 9 次(单词数),所以它甚至可能产生 36 个元素。这可能意味着您以错误的方式计算它。可能您将每个值单独保存 - 对于每个文档分隔值。但是对于每个单词,您应该从所有文档中累积值。也许您应该将结果保留为values[word] = [result1, result2],然后使用它们全部计算(term_freq) * (idf_)

标签: python scikit-learn nlp tfidfvectorizer


【解决方案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

【讨论】:

    猜你喜欢
    • 2020-09-24
    • 2018-05-04
    • 2018-06-22
    • 2020-03-18
    • 1970-01-01
    • 2020-05-17
    • 2019-08-05
    • 1970-01-01
    • 2014-06-17
    相关资源
    最近更新 更多