上篇: 用go构建个简单的搜索(七) BM25打分
流程概述
graph TD
A(加载文档) --> b[切词]
b[切词] --> c[统计文档和词频关系]
d[BM25关键词打分] --> e[打分排序]
f[关键词查询]
当前处理点和遇到的问题
- demo是用map处理的,map好处是可以用到k-v数据库作为持久化
- go的map根据value排序 map[int]float32=》文档:得分
- go中map的v是结构体数据修改
困惑的地方
- 文档是不停增长的,但是BM25打分依赖文档总数和至少出现一次的关键词总数 该如何处理比较好点
- 如何定义一个父类的结构体,实现自定义的子结构体数据处理
测试效果
代码demo
type docWord struct {
word_s string //关键词
word_tf map[int]int //文档中的词频 key文档编号
word_score map[int]float32 //文档中得分 key文档编号
score_sort []scoreSort //得分排序
}
//得分排序
type scoreSort struct {
id int
score float32
}
func TestBM26(t *testing.T) {
//测试每行作为一个文档
str, isTrue := loadFileByLine("D:/开发语言/文章/小说/活着2.txt")
if isTrue {
avgLen := getAVG(str) //得到文档平均长度
var docWordArry = make(map[string]docWord) //切词对应的数据文档
//单词 文档 打分
for i, s := range str {
wordTF := getSplicWordTF(s)//切词
//至少出现一次文档数 得到词频
for word := range wordTF {
_, ok := docWordArry[word]
if ok {
docWordArry[word].word_tf[i]++
} else {
docWordArry[word] = docWord{
word_s: word,
word_tf: make(map[int]int),
word_score: make(map[int]float32),
score_sort: make([]scoreSort, 0),
}
docWordArry[word].word_tf[i] = 1
}
}
}
for w := range docWordArry {
dw := docWordArry[w]
for index := range dw.word_tf {
//打分
dw.word_score[index] = BM25(float32(dw.word_tf[index]), float32(len([]rune(str[index]))), avgLen, float32(len(str)), float32(len(dw.word_tf)))
dw.score_sort = append(dw.score_sort, scoreSort{index, dw.word_score[index]})
}
// if "福贵" == w {
//得分排序
sort.Slice(dw.score_sort, func(i, j int) bool {
return dw.score_sort[i].score > dw.score_sort[j].score // 降序
})
// }
docWordArry[w] = dw //这里有个map修改值得问题
}
search := docWordArry["福贵"]
for _, index := range search.score_sort {
fmt.Printf("%sn文档ID:%d,词:%s,打分:%fn", str[index.id], index.id, search.word_s, search.word_score[index.id])
}
}
}