CDQA——BM25算法

  • 自己在看这个开源代码中看到了这个相似性算法和一些工程中的技巧感觉很不错,算是多了点儿见识,以前还从没有用过稀疏矩阵这个存储结构,这里就写一个文档简单记录一下

python小知识

  • Python中关于eval函数与ast.literal_eval使用的区别介绍(图文)
  • https://www.php.cn/python-tutorials-376459.html
  • numpy的广播机制(具体没有用,算是回顾一下):
  • https://www.cnblogs.com/jiaxin359/p/9021726.html

BM25

在这里插入图片描述

  • 首先,我们抛却一切,需要知道这个算法是什么,推导过程以及背景什么的就不叙述了,可以参考但不限于 这篇博客
  • 归根结底,我们的算法其实就是一个计算公式,如下:

在这里插入图片描述

普通代码

在这里插入图片描述

代码工程优化

  • 这个开源库一共从两个方面优化了计算方式,具体可以测试跑跑代码,个人测试数据就是CDQA这个开源库的数据
    1. 稀疏存储: 节约空间
    1. 向量化矩阵计算:加速以及简洁
    1. 在上述向量化的基础上,作者只针对非零元素进行计算bm分数,进一步加速计算速度。

代码

  • 原始库里的代码逻辑如图:

在这里插入图片描述

  • 自己把这块代码单独从库里提出来实验了一下,自己注释的有点乱

依赖和超参

import numpy as np
import scipy.sparse as sp
from sklearn.utils.validation import check_is_fitted, check_array, FLOAT_DTYPES
from sklearn.feature_extraction.text import _document_frequency
from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd
from ast import literal_eval

use_idf = True
floor = None
k1 = 2.0
b = 0.75
norm = None

主体函数

def fit(X):
    """
    Parameters
    ----------
    X : sparse matrix, [n_samples, n_features]
        document-term matrix
    """
    X = check_array(X, accept_sparse=("csr", "csc"))

    # x是否为sparse类型
    # 压缩稀疏列矩阵
    if not sp.issparse(X):
        X = sp.csc_matrix(X)

    # 如果用idf
    if use_idf:
        n_samples, n_features = X.shape

        # (57164,),所有文档对应单词的频率
        df = _document_frequency(X)

        # (57164,)
        idf = np.log((n_samples - df + 0.5) / (df + 0.5))

        if floor is not None:
            idf = idf * (idf > floor) + floor * (idf < floor)

        # 创建对角稀疏矩阵,其实就是一维变成2维
        _idf_diag = sp.spdiags(idf, diags=0, m=n_features, n=n_features)
        print(_idf_diag.shape)

    # Create BM25 features

    # Document length (number of terms) in each row
    # Shape is (n_samples, 1)
    dl = X.sum(axis=1)

    # Number of non-zero elements in each row
    # Shape is (n_samples, )
    sz = X.indptr[1:] - X.indptr[0:-1]

    # In each row, repeat `dl` for `sz` times
    # Shape is (sum(sz), )
    # Example
    # -------
    # dl = [4, 5, 6]
    # sz = [1, 2, 3]
    # rep = [4, 5, 5, 6, 6, 6]
    
    # 每一个单词对应的文档长度
    rep = np.repeat(np.asarray(dl), sz)
    print(rep.shape)
    
    
    # Average document length
    # Scalar value
    avgdl = np.average(dl)

    # Compute BM25 score only for non-zero elements
    # 实验一下整个计算
    
    
    
# X: 每个文档中每一个单词的个数
#     print(X.shape)
#     print(X.data.shape)
#     print(rep.shape)
#     print(avgdl.shape)

    # (非零元素_num,)
    #  X.data表示的是其中非零元素,算是加速计算
    data = (
        X.data
        * (k1 + 1)
        / (X.data + k1 * (1 - b + b * rep / avgdl))
    )

	# 恢复结构
    X = sp.csr_matrix((data, X.indices, X.indptr), shape=X.shape)

    if norm:
        X = normalize(X, norm=norm, copy=False)

    _doc_matrix = X
    
    return _doc_matrix

函数调用

# 出于安全考虑,对字符串进行类型转换的时候,最好使用ast.literal_eval()函数!

rdf = pd.read_csv('/home/lixiang/桌面/cdqa/cdQA-master/path-to-directory/bnpp_newsroom-v1.1.csv', converters={'paragraphs': literal_eval})

raw_documents = rdf["paragraphs"].apply(lambda x: " ".join(x))



wow = CountVectorizer(    input="content",
        encoding="utf-8",
        decode_error="strict",
        strip_accents=None,
        lowercase=True,
        preprocessor=None,
        tokenizer=None,
        analyzer="word",
        stop_words=None,
        token_pattern=r"(?u)\b\w\w+\b",
        ngram_range=(1, 2),
        max_df=1.0,
        min_df=1,
        max_features=None,
        vocabulary=None,
        binary=False,
        dtype=np.float64)


X = wow.fit_transform(raw_documents=raw_documents)

# 传入的是:每一个样本,对应单词的个数
a = fit(X)

END

  • 本文没有写过多细节的介绍,工程这个东西还是需要读源码自己体会一下。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值