蛙蛙牌自動提取Tag算法
摘要:Tag系統是Web2.0的一個招牌應用,如果你有一個經營了好幾年的論壇,是不是也想生成自己的一套TAG。別聽他們說什麼語義WEB,文本聚類算法,TIIDF,餘弦定理,相似度算法啥的高深算法(我一個也沒整明白),跟我來,簡單的計算詞頻來提取tag的效果就很好。
分析;把每個帖子進行分詞,然後把詞的出現頻率倒序排列,取出前N個就作爲TAG了。當然要一個板塊一個板塊的提取tag,如果把軍事板塊和情感板塊的帖子混雜在一起提取tag,提取出來的tag相關性比較差一些,如果分開提取,相關性要好一些,整體效果好。好多時候做訓練算法,語料很重要。先分詞吧,自己寫分詞算法也是弄個詞庫,自己用正向最大匹配來分詞,或者兩個兩個字的來當詞,所以還不如直接用中科院那套呢,直接使用了隱式馬爾可夫算法,效果雖說不是很好吧,也能滿足需求了,對吧。具體測試代碼、分詞組件、詞庫下載見以下鏈接
http://www.cnblogs.com/edison1024/archive/2006/05/03/390832.html
得點了他那個廣告才能顯示下載地址,你就點吧,人家提供下載也不容易。分詞後要去除停止詞,停止詞自己從網上搜索一份,如果不去除停止詞,最後肯定是“了”,“的”,“我”等詞出現的頻率最高,你不會把這些常用詞做tags吧,呵呵。當然NICTCLAS是可以標註詞性的,你可以分詞後把語氣詞、副詞等虛詞去了,這樣更好一些,但我就懶得做了,直接分詞、去除停止詞兩步。
完了計算每個詞出現的頻率就好說了,弄一個全局的字典,每個詞出現一次增加一個計數,第一次出現先添加到字典,並計數爲0,最後把出現次數在某個閾值以上的詞插入到數據庫裏,這就是你要的tag了,先來看一下我的效果吧(大家別笑哦,我是從一個美女貼圖論壇提取了一些帖子的主題當語料的,爲了不降低博客園的PR值,就貼圖,不貼文字了)。
開始上代碼
先貼分詞
{
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("初始化分詞器錯誤:{0}", ex);
}
}
/// <summary>
/// 分詞並去除停止詞
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static IEnumerable<string> SplitWords(string input)
{
Console.WriteLine(input);
//預處理,不處理那個分詞組件有可能內存讀寫錯誤,那玩意兒寫的不太健壯,容錯性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)
{
//錯誤的時候除了打出錯誤詳細信息後打出出錯的上下文,傳入的參數,臨時變量等有助於從trace裏分析錯誤,要不死了也不知道怎麼死的
Console.WriteLine("{0}/r/n{1}",input,ex);
}
return ret;
}
}
}
計算詞頻
{
//大字典,保存每個詞的詞頻,key是詞,value是詞頻
static Dictionary<string,int> _hashlist = new Dictionary<string, int>(10240);
public static void Excute()
{
//1、取出帖子,越多越好,越多提取的準確性越高
IEnumerable<string> source = Dao.GetPostTitles();
foreach (string str in source)
{
//2、把每個帖子主題分詞
IEnumerable<string> words = WawaSplitWorder.SplitWords(str);
if(words == null)
continue;
//3、把每個詞插入到大字典裏,以前存在就把詞頻加1
foreach (string word in words)
{
if(_hashlist.ContainsKey(word))
{
_hashlist[word]++;
}
else
{
_hashlist.Add(word,0);
}
}
}
//4、把大於某個閾值(這裏是20)的詞插入數據
foreach (KeyValuePair<string, int> pair in _hashlist)
{
//如果一次循環插入幾萬個詞,SQLSERVE每秒提交的批會很高,有可能CPU瞬間很高,Sleep(0)能讓CPU長得慢點兒,Sleep(1)也行,不過我不知道這兩個的區別。或者直接 用sqlserver的bulkcopy 性能也8錯
Thread.Sleep(0);
if (pair.Value > 20)
{
Console.WriteLine("{0}-{1}",pair.Key,pair.Value);
Dao.addtags(pair.Key, pair.Value);
}
}
}
}
代碼寫的比較糙,大家湊合看,都是隨手寫的。最後寫一個sql查出tag並按詞頻倒序排列,選出一個datatable,用datalist一綁定就O了。當然了,我這是提取標籤的土法,大師們看了別吐,呵呵。