gse 一个高效的分词工具

1、gse 简介

Go 高性能多语言 NLP 和分词, 支持英文、中文、日文等, 支持接入 elasticsearchbleve

Gse 是结巴分词(jieba)的 golang 实现, 并尝试添加 NLP 功能和更多属性

特征

  • 支持普通、搜索引擎、全模式、精确模式和 HMM 模式多种分词模式
  • 支持自定义词典、embed 词典、词性标注、停用词、整理分析分词
  • 多语言支持: 英文, 中文, 日文等
  • 支持繁体字
  • NLP 和 TensorFlow 支持 (进行中)
  • 命名实体识别 (进行中)
  • 支持接入 Elasticsearch 和 bleve
  • 可运行 JSON RPC 服务

算法

  • 词典用双数组 trie(Double-Array Trie)实现,
  • 分词器算法为基于词频的最短路径加动态规划, 以及 DAG 和 HMM 算法分词.
  • 支持 HMM 分词, 使用 viterbi 算法.

分词速度

  • 单线程 9.2MB/s
  • goroutines 并发 26.8MB/s.
  • HMM 模式单线程分词速度 3.2MB/s.(双核 4 线程 Macbook Pro)。

安装/更新

需要 Go 1.11+, just import:

1
import "github.com/go-ego/gse"

Otherwise, to install the gse package, run the command:

1
go get -u github.com/go-ego/gse

2、使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package main

import (
"flag"
"fmt"
"log"
"regexp"

"github.com/go-ego/gse"
"github.com/go-ego/gse/hmm/idf"
"github.com/go-ego/gse/hmm/pos"
)

var (
seg gse.Segmenter
posSeg pos.Segmenter

text = "《复仇者联盟3:无限战争》是全片使用IMAX摄影机拍摄制作的的科幻片."
text1 = flag.String("text", text, "要分词的文本")

text2 = "西雅图地标建筑, Seattle Space Needle, 西雅图太空针. Sky tree."
)

func main() {
flag.Parse()

// 加载默认词典
seg.LoadDict()
// 加载默认 embed 词典
// seg.LoadDictEmbed()

// 加载简体中文词典
// seg.LoadDict("zh_s")
// seg.LoadDictEmbed("zh_s")

// 加载繁体中文词典
// seg.LoadDict("zh_t")

// 加载日文词典
// seg.LoadDict("jp")

// seg.LoadDict("../data/dict/dictionary.txt")

// Loading the custom dictionary
// seg.LoadDict("zh,../../testdata/zh/test_dict.txt,../../testdata/zh/test_dict1.txt")

addToken() // 添加分词
cut() // 使用 DAG 或 HMM 模式分词
cutPos()
segCut() // 使用最短路径和动态规划分词

extAndRank(seg)
}

// 添加分词
func addToken() {
err := seg.AddToken("《复仇者联盟3:无限战争》", 100, "n")
fmt.Println("add token: ", err)
seg.AddToken("西雅图中心", 100)
seg.AddToken("西雅图太空针", 100, "n")
seg.AddToken("Space Needle", 100, "n")
// seg.AddTokenForce("上海东方明珠广播电视塔", 100, "n")

seg.AddToken("太空针", 100)
seg.ReAddToken("太空针", 100, "n")
freq, pos, ok := seg.Find("太空针")
fmt.Println("seg.Find: ", freq, pos, ok)

// seg.CalcToken()
err = seg.RemoveToken("太空针")
fmt.Println("remove token: ", err)
}

// 使用 DAG 或 HMM 模式分词
func cut() {
// "《复仇者联盟3:无限战争》是全片使用IMAX摄影机拍摄制作的的科幻片."

// use DAG and HMM
hmm := seg.Cut(text, true)
fmt.Println("cut use hmm: ", hmm)
// cut use hmm: [《复仇者联盟3:无限战争》 是 全片 使用 imax 摄影机 拍摄 制作 的 的 科幻片 .]

cut := seg.Cut(text)
fmt.Println("cut: ", cut)
// cut: [《 复仇者 联盟 3 : 无限 战争 》 是 全片 使用 imax 摄影机 拍摄 制作 的 的 科幻片 .]

hmm = seg.CutSearch(text, true)
fmt.Println("cut search use hmm: ", hmm)
//cut search use hmm: [复仇 仇者 联盟 无限 战争 复仇者 《复仇者联盟3:无限战争》 是 全片 使用 imax 摄影 摄影机 拍摄 制作 的 的 科幻 科幻片 .]
fmt.Println("analyze: ", seg.Analyze(hmm, text))

cut = seg.CutSearch(text)
fmt.Println("cut search: ", cut)
// cut search: [《 复仇 者 复仇者 联盟 3 : 无限 战争 》 是 全片 使用 imax 摄影 机 摄影机 拍摄 制作 的 的 科幻 片 科幻片 .]

cut = seg.CutAll(text)
fmt.Println("cut all: ", cut)
// cut all: [《复仇者联盟3:无限战争》 复仇 复仇者 仇者 联盟 3 : 无限 战争 》 是 全片 使用 i m a x 摄影 摄影机 拍摄 摄制 制作 的 的 科幻 科幻片 .]

s := seg.CutStr(cut, ", ")
fmt.Println("cut all to string: ", s)
// cut all to string: 《复仇者联盟3:无限战争》, 复仇, 复仇者, 仇者, 联盟, 3, :, 无限, 战争, 》, 是, 全片, 使用, i, m, a, x, 摄影, 摄影机, 拍摄, 摄制, 制作, 的, 的, 科幻, 科幻片, .

analyzeAndTrim(cut)

reg := regexp.MustCompile(`(\d+年|\d+月|\d+日|[\p{Latin}]+|[\p{Hangul}]+|\d+\.\d+|[a-zA-Z0-9]+)`)
text1 := `헬로월드 헬로 서울, 2021年09月10日, 3.14`
hmm = seg.CutDAG(text1, reg)
fmt.Println("Cut with hmm and regexp: ", hmm, hmm[0], hmm[6])
}

func analyzeAndTrim(cut []string) {
a := seg.Analyze(cut, "")
fmt.Println("analyze the segment: ", a)
// analyze the segment:

cut = seg.Trim(cut)
fmt.Println("cut all: ", cut)
// cut all: [复仇者联盟3无限战争 复仇 复仇者 仇者 联盟 3 无限 战争 是 全片 使用 i m a x 摄影 摄影机 拍摄 摄制 制作 的 的 科幻 科幻片]

fmt.Println(seg.String(text2, true))
// 西雅图/nr 地标/n 建筑/n ,/x /x seattle/x /x space needle/n ,/x /x 西雅图太空针/n ./x /x sky/x /x tree/x ./x
fmt.Println(seg.Slice(text2, true))
// [西雅图 地标 建筑 , seattle space needle , 西雅图太空针 . sky tree .]
}

func cutPos() {
// "西雅图地标建筑, Seattle Space Needle, 西雅图太空针. Sky tree."

po := seg.Pos(text2, true)
fmt.Println("pos: ", po)
// pos: [{西雅图 nr} {地标 n} {建筑 n} {, x} { x} {seattle x} { x} {space needle n} {, x} { x} {西雅图太空针 n} {. x} { x} {sky x} { x} {tree x} {. x}]

po = seg.TrimWithPos(po, "zg")
fmt.Println("trim pos: ", po)
// trim pos: [{西雅图 nr} {地标 n} {建筑 n} {, x} { x} {seattle x} { x} {space needle n} {, x} { x} {西雅图太空针 n} {. x} { x} {sky x} { x} {tree x} {. x}]

posSeg.WithGse(seg)
po = posSeg.Cut(text, true)
fmt.Println("pos: ", po)
// pos: [{《 x} {复仇 v} {者 k} {联盟 j} {3 x} {: x} {无限 v} {战争 n} {》 x} {是 v} {全片 n} {使用 v} {imax eng} {摄影 n} {机 n} {拍摄 v} {制作 vn} {的的 u} {科幻 n} {片 q} {. m}]

po = posSeg.TrimWithPos(po, "zg")
fmt.Println("trim pos: ", po)
// trim pos: [{《 x} {复仇 v} {者 k} {联盟 j} {3 x} {: x} {无限 v} {战争 n} {》 x} {是 v} {全片 n} {使用 v} {imax eng} {摄影 n} {机 n} {拍摄 v} {制作 vn} {的的 u} {科幻 n} {片 q} {. m}]
}

// 使用最短路径和动态规划分词
func segCut() {
segments := seg.Segment([]byte(*text1))
fmt.Println(gse.ToString(segments, true))
// 《/x 复仇/v 者/k 复仇者/n 联盟/j 3/x :/x 无限/v 战争/n 》/x 是/v 全片/n 使用/v imax/x 摄影/n 机/n 摄影机/n 拍摄/v 制作/vn 的/uj 的/uj 科幻/n 片/q 科幻片/n ./x

segs := seg.Segment([]byte(text2))
// log.Println(gse.ToString(segs, false))
log.Println(gse.ToString(segs))
// 西雅图/nr 地标/n 建筑/n ,/x /x seattle/x /x space needle/n ,/x /x 西雅图太空针/n ./x /x sky/x /x tree/x ./x

// 搜索模式主要用于给搜索引擎提供尽可能多的关键字
// segs := seg.ModeSegment(text2, true)
log.Println("搜索模式: ", gse.ToString(segs, true))
// 搜索模式: 西雅图/nr 地标/n 建筑/n ,/x /x seattle/x /x space needle/n ,/x /x 西雅图太空针/n ./x /x sky/x /x tree/x ./x

log.Println("to slice", gse.ToSlice(segs, true))
// to slice [西雅图 地标 建筑 , seattle space needle , 西雅图太空针 . sky tree .]
}

func extAndRank(segs gse.Segmenter) {
var te idf.TagExtracter
te.WithGse(segs)
err := te.LoadIdf()
fmt.Println("load idf: ", err)

segments := te.ExtractTags(text, 5)
fmt.Println("segments: ", len(segments), segments)
// segments: 5 [{科幻片 1.6002581704125} {全片 1.449761569875} {摄影机 1.2764747747375} {拍摄 0.9690261695075} {制作 0.8246043033375}]

var tr idf.TextRanker
tr.WithGse(segs)

results := tr.TextRank(text, 5)
fmt.Println("results: ", results)
// results: [{机 1} {全片 0.9931964427972227} {摄影 0.984870660504368} {使用 0.9769826633059524} {是 0.8489363954683677}]
}

3、过滤

1
2
3
4
5
6
// 过滤 html 标签
value := gse.FilterHtml(in)
// 过滤 Emoji
value = gse.FilterEmoji(value)
// 过滤 标点符号
value = gse.FilterSymbol(value)

gse 一个高效的分词工具
https://flepeng.github.io/021-Go-33-Go-第三方包-gse-一个高效的分词工具/
作者
Lepeng
发布于
2024年12月9日
许可协议