蛙蛙牌自動(dòng)提取Tag算法
摘要:Tag系統(tǒng)是Web2.0的一個(gè)招牌應(yīng)用,如果你有一個(gè)經(jīng)營(yíng)了好幾年的論壇,是不是也想生成自己的一套TAG。別聽他們說什么語(yǔ)義WEB,文本聚類算法,TIIDF,余弦定理,相似度算法啥的高深算法(我一個(gè)也沒整明白),跟我來,簡(jiǎn)單的計(jì)算詞頻來提取tag的效果就很好。
分析;把每個(gè)帖子進(jìn)行分詞,然后把詞的出現(xiàn)頻率倒序排列,取出前N個(gè)就作為TAG了。當(dāng)然要一個(gè)板塊一個(gè)板塊的提取tag,如果把軍事板塊和情感板塊的帖子混雜在一起提取tag,提取出來的tag相關(guān)性比較差一些,如果分開提取,相關(guān)性要好一些,整體效果好。好多時(shí)候做訓(xùn)練算法,語(yǔ)料很重要。先分詞吧,自己寫分詞算法也是弄個(gè)詞庫(kù),自己用正向最大匹配來分詞,或者兩個(gè)兩個(gè)字的來當(dāng)詞,所以還不如直接用中科院那套呢,直接使用了隱式馬爾可夫算法,效果雖說不是很好吧,也能滿足需求了,對(duì)吧。具體測(cè)試代碼、分詞組件、詞庫(kù)下載見以下鏈接
http://www.cnblogs.com/edison1024/archive/2006/05/03/390832.html
得點(diǎn)了他那個(gè)廣告才能顯示下載地址,你就點(diǎn)吧,人家提供下載也不容易。分詞后要去除停止詞,停止詞自己從網(wǎng)上搜索一份,如果不去除停止詞,最后肯定是“了”,“的”,“我”等詞出現(xiàn)的頻率最高,你不會(huì)把這些常用詞做tags吧,呵呵。當(dāng)然NICTCLAS是可以標(biāo)注詞性的,你可以分詞后把語(yǔ)氣詞、副詞等虛詞去了,這樣更好一些,但我就懶得做了,直接分詞、去除停止詞兩步。
完了計(jì)算每個(gè)詞出現(xiàn)的頻率就好說了,弄一個(gè)全局的字典,每個(gè)詞出現(xiàn)一次增加一個(gè)計(jì)數(shù),第一次出現(xiàn)先添加到字典,并計(jì)數(shù)為0,最后把出現(xiàn)次數(shù)在某個(gè)閾值以上的詞插入到數(shù)據(jù)庫(kù)里,這就是你要的tag了,先來看一下我的效果吧(大家別笑哦,我是從一個(gè)美女貼圖論壇提取了一些帖子的主題當(dāng)語(yǔ)料的,為了不降低博客園的PR值,就貼圖,不貼文字了)。
開始上代碼
先貼分詞namespace WawaSoft.Search.Common
{
public sealed class WawaSplitWorder
{
static List<string> _stopWords = new List<string>();
static NICTCLAS _nictclas;
public static void Init()
{
try
{
//1、初始化分詞器
_nictclas = new NICTCLAS();
_nictclas.OperateType = eOperateType.OnlySegment;
_nictclas.OutputFormat = eOutputFormat.PKU;
//2、加載停止詞
using (StreamReader sr =
new StreamReader("data\\StopWords.txt", Encoding.Default))
{
string temp;
while ((temp = sr.ReadLine()) != null)
{
_stopWords.Add(temp);
}
}
}
catch (Exception ex)
{
Trace.TraceError("初始化分詞器錯(cuò)誤:{0}", ex);
}
}
/**//// <summary>
/// 分詞并去除停止詞
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static IEnumerable<string> SplitWords(string input)
{
Console.WriteLine(input);
//預(yù)處理,不處理那個(gè)分詞組件有可能內(nèi)存讀寫錯(cuò)誤,那玩意兒寫的不太健壯,容錯(cuò)性8行的說,呵呵
input = input.Replace("/", "");
input = input.Replace(".", "");
string result = string.Empty;
List<string> ret = null;
try
{
//1、分詞
_nictclas.ParagraphProcessing(input, ref result);
ret = new List<string>(
result.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries));
//2、去除干擾詞
List<string> needRemove = new List<string>();
foreach (string word in ret)
{
foreach (string s in _stopWords)
{
if (string.Compare(s, word, false) == 0)
{
needRemove.Add(word);
break;
}
}
}
foreach (string removeWord in needRemove)
{
ret.Remove(removeWord);
}
}
catch (Exception ex)
{
//錯(cuò)誤的時(shí)候除了打出錯(cuò)誤詳細(xì)信息后打出出錯(cuò)的上下文,傳入的參數(shù),臨時(shí)變量等有助于從trace里分析錯(cuò)誤,要不死了也不知道怎么死的
Console.WriteLine("{0}\r\n{1}",input,ex);
}
return ret;
}
}
}
計(jì)算詞頻class AutoGenTag
{
//大字典,保存每個(gè)詞的詞頻,key是詞,value是詞頻
static Dictionary<string,int> _hashlist = new Dictionary<string, int>(10240);
public static void Excute()
{
//1、取出帖子,越多越好,越多提取的準(zhǔn)確性越高
IEnumerable<string> source = Dao.GetPostTitles();
foreach (string str in source)
{
//2、把每個(gè)帖子主題分詞
IEnumerable<string> words = WawaSplitWorder.SplitWords(str);
if(words == null)
continue;
//3、把每個(gè)詞插入到大字典里,以前存在就把詞頻加1
foreach (string word in words)
{
if(_hashlist.ContainsKey(word))
{
_hashlist[word]++;
}
else
{
_hashlist.Add(word,0);
}
}
}
//4、把大于某個(gè)閾值(這里是20)的詞插入數(shù)據(jù)
foreach (KeyValuePair<string, int> pair in _hashlist)
{
//如果一次循環(huán)插入幾萬個(gè)詞,SQLSERVE每秒提交的批會(huì)很高,有可能CPU瞬間很高,Sleep(0)能讓CPU長(zhǎng)得慢點(diǎn)兒,Sleep(1)也行,不過我不知道這兩個(gè)的區(qū)別?;蛘咧苯?nbsp;用sqlserver的bilkcopy性能也8錯(cuò)
Thread.Sleep(0);
if (pair.Value > 20)
{
Console.WriteLine("{0}-{1}",pair.Key,pair.Value);
Dao.addtags(pair.Key, pair.Value);
}
}
}
}
代碼寫的比較糙,大家湊合看,都是隨手寫的。最后寫一個(gè)sql查出tag并按詞頻倒序排列,選出一個(gè)datatable,用datalist一綁定就O了。當(dāng)然了,我這是提取標(biāo)簽的土法,大師們看了別吐,呵呵。