BotSharp/BotSharp.Core/Engines/Jieba.NET/FinalSeg/Viterbi.cs
Oceania2018 6019d9268f 1. Embed jieba.NET source code.
2. Remove publish profile.
3. Fix docker build
2018-09-21 06:02:58 -05:00

168 lines
5.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using JiebaNet.Segmenter.Common;
using Newtonsoft.Json;
namespace JiebaNet.Segmenter.FinalSeg
{
public class Viterbi : IFinalSeg
{
private static readonly Lazy<Viterbi> Lazy = new Lazy<Viterbi>(() => new Viterbi());
private static readonly char[] States = { 'B', 'M', 'E', 'S' };
private static readonly Regex RegexChinese = new Regex(@"([\u4E00-\u9FD5]+)", RegexOptions.Compiled);
private static readonly Regex RegexSkip = new Regex(@"([a-zA-Z0-9]+(?:\.\d+)?%?)", RegexOptions.Compiled);
private static IDictionary<char, IDictionary<char, double>> _emitProbs;
private static IDictionary<char, double> _startProbs;
private static IDictionary<char, IDictionary<char, double>> _transProbs;
private static IDictionary<char, char[]> _prevStatus;
private Viterbi()
{
LoadModel();
}
// TODO: synchronized
public static Viterbi Instance
{
get { return Lazy.Value; }
}
public IEnumerable<string> Cut(string sentence)
{
var tokens = new List<string>();
foreach (var blk in RegexChinese.Split(sentence))
{
if (RegexChinese.IsMatch(blk))
{
tokens.AddRange(ViterbiCut(blk));
}
else
{
var segments = RegexSkip.Split(blk).Where(seg => !string.IsNullOrEmpty(seg));
tokens.AddRange(segments);
}
}
return tokens;
}
#region Private Helpers
private void LoadModel()
{
var stopWatch = new Stopwatch();
stopWatch.Start();
_prevStatus = new Dictionary<char, char[]>()
{
{'B', new []{'E', 'S'}},
{'M', new []{'M', 'B'}},
{'S', new []{'S', 'E'}},
{'E', new []{'B', 'M'}}
};
_startProbs = new Dictionary<char, double>()
{
{'B', -0.26268660809250016},
{'E', -3.14e+100},
{'M', -3.14e+100},
{'S', -1.4652633398537678}
};
var transJson = FileExtension.ReadEmbeddedAllLine(ConfigManager.ProbTransFile);
_transProbs = JsonConvert.DeserializeObject<IDictionary<char, IDictionary<char, double>>>(transJson);
var emitJson = FileExtension.ReadEmbeddedAllLine(ConfigManager.ProbEmitFile);
_emitProbs = JsonConvert.DeserializeObject<IDictionary<char, IDictionary<char, double>>>(emitJson);
stopWatch.Stop();
Debug.WriteLine("model loading finished, time elapsed {0} ms.", stopWatch.ElapsedMilliseconds);
}
private IEnumerable<string> ViterbiCut(string sentence)
{
var v = new List<IDictionary<char, double>>();
IDictionary<char, Node> path = new Dictionary<char, Node>();
// Init weights and paths.
v.Add(new Dictionary<char, double>());
foreach (var state in States)
{
var emP = _emitProbs[state].GetDefault(sentence[0], Constants.MinProb);
v[0][state] = _startProbs[state] + emP;
path[state] = new Node(state, null);
}
// For each remaining char
for (var i = 1; i < sentence.Length; ++i)
{
IDictionary<char, double> vv = new Dictionary<char, double>();
v.Add(vv);
IDictionary<char, Node> newPath = new Dictionary<char, Node>();
foreach (var y in States)
{
var emp = _emitProbs[y].GetDefault(sentence[i], Constants.MinProb);
Pair<char> candidate = new Pair<char>('\0', double.MinValue);
foreach (var y0 in _prevStatus[y])
{
var tranp = _transProbs[y0].GetDefault(y, Constants.MinProb);
tranp = v[i - 1][y0] + tranp + emp;
if (candidate.Freq <= tranp)
{
candidate.Freq = tranp;
candidate.Key = y0;
}
}
vv[y] = candidate.Freq;
newPath[y] = new Node(y, path[candidate.Key]);
}
path = newPath;
}
var probE = v[sentence.Length - 1]['E'];
var probS = v[sentence.Length - 1]['S'];
var finalPath = probE < probS ? path['S'] : path['E'];
var posList = new List<char>(sentence.Length);
while (finalPath != null)
{
posList.Add(finalPath.Value);
finalPath = finalPath.Parent;
}
posList.Reverse();
var tokens = new List<string>();
int begin = 0, next = 0;
for (var i = 0; i < sentence.Length; i++)
{
var pos = posList[i];
if (pos == 'B')
begin = i;
else if (pos == 'E')
{
tokens.Add(sentence.Sub(begin, i + 1));
next = i + 1;
}
else if (pos == 'S')
{
tokens.Add(sentence.Sub(i, i + 1));
next = i + 1;
}
}
if (next < sentence.Length)
{
tokens.Add(sentence.Substring(next));
}
return tokens;
}
#endregion
}
}