對盜圖、盜文、盜墓深惡痛絕嗎?PostgreSQL結合餘弦、線性相關演算法 在文字、圖片、陣列相似 等領域的應用 - 1 理論 - tf/idf...
標籤
PostgreSQL , 文字分析 , tf , idf , tf-idf , tag
背景
很多網站有標籤的功能,會根據網頁自動生成標籤,標籤實際上就是該網頁的關鍵詞,比如一個賣手機的網頁,那麼標籤是如何生成的呢?
在一篇文件裡面,是不是出現越多的詞,就越是關鍵詞呢?
比如在中文裡面的、是、我、你可能出現次數是比較多的,它們很顯然不是關鍵詞,這些屬於stop word,是需要被忽略的。
另外還有一些詞,雖然不是stop word,但是也可能不算關鍵詞,比如應用、科學、普通話,等等一些在很多文章中都可能出現的詞,可能不是關鍵詞。
有什麼好的演算法能提取文章的關鍵詞呢?
TF-IDF演算法
TF-IDF是一種統計方法,用以評估一字詞對於一個檔案集或一個語料庫中的其中一份檔案的重要程度。字詞的重要性隨著它在檔案中出現的次數成正比增加,但同時會隨著它在語料庫中出現的頻率成反比下降。
TF-IDF加權的各種形式常被搜尋引擎應用,作為檔案與使用者查詢之間相關程度的度量或評級。除了TF-IDF以外,因特網上的搜尋引擎還會使用基於連結分析的評級方法,以確定檔案在搜尋結果中出現的順序。
1. tf比較好理解,就是一個詞在當前文字的出現頻率,後面會有例子。
2. idf是一個加權,逆向檔案頻率(inverse document frequency,IDF)是一個詞語普遍重要性的度量。也就是說一些普通詞,因此越普通的詞,它的IDF越低。
某一特定詞語的IDF,可以由總檔案數目除以包含該詞語之檔案的數目,再取對數得到IDF。
3. 計算一個詞在文件中的關鍵性,計算TF與IDF的乘積即得到。因此,TF-IDF傾向於過濾掉常見的詞語,保留重要的詞語。
舉例
例1
有很多不同的數學公式可以用來計算TF-IDF。
這邊的例子以上述的數學公式來計算。
1. 詞頻 (TF) 是一詞語出現的次數除以該檔案的總詞語數。
假如一篇檔案的總詞語數是100個,而詞語“母牛”出現了3次,那麼“母牛”一詞在該檔案中的詞頻就是3/100=0.03。
2. 一個計算檔案頻率 (IDF) 的方法是檔案集裡包含的檔案總數除以測定有多少份檔案出現過“母牛”一詞。
所以,如果“母牛”一詞在1,000份檔案出現過,而檔案總數是10,000,000份的話,其逆向檔案頻率就是 log(10,000,000 / 1,000)=4。
3. 最後“母牛”的TF-IDF的分數為0.03 * 4=0.12。
TF-IDF越高,說明這個詞在該文字中越關鍵。
例2
在某個一共有一千詞的網頁中“原子能”、“的”和“應用”分別出現了 2 次、35 次 和 5 次,那麼它們的詞頻就分別是 0.002、0.035 和 0.005。
我們將這三個數相加,其和 0.042 就是相應網頁和查詢“原子能的應用” 相關性的一個簡單的度量。
概括地講,如果一個查詢包含關鍵詞 w1,w2,...,wN, 它們在一篇特定網頁中的詞頻分別是: TF1, TF2, ..., TFN。 (TF: term frequency)。 那麼,這個查詢和該網頁的相關性就是:TF1 + TF2 + ... + TFN。
讀者可能已經發現了又一個漏洞。
在上面的例子中,詞“的”佔了總詞頻的 80% 以上,而它對確定網頁的主題幾乎沒有用。我們稱這種詞叫“應刪除詞”(Stopwords),也就是說在度量相關性是不應考慮它們的頻率。在漢語中,應刪除詞還有“是”、“和”、“中”、“地”、“得”等等幾十個。
忽略這些應刪除詞後,上述網頁的相似度就變成了0.007,其中“原子能”貢獻了 0.002,“應用”貢獻了 0.005。
細心的讀者可能還會發現另一個小的漏洞。
在漢語中,“應用”是個很通用的詞,而“原子能”是個很專業的詞,後者在相關性排名中比前者重要。因此我們需要給漢語中的每一個詞給一個權重(IDF),這個權重的設定必須滿足下面兩個條件:
1. 一個詞預測主題能力越強,權重就越大,反之,權重就越小。我們在網頁中看到“原子能”這個詞,或多或少地能瞭解網頁的主題。我們看到“應用”一次,對主題基本上還是一無所知。因此,“原子能“的權重就應該比應用大。
2. 應刪除詞的權重應該是零。
我們很容易發現,如果一個關鍵詞只在很少的網頁中出現,我們通過它就容易鎖定搜尋目標,它的權重也就應該大。
反之如果一個詞在大量網頁中出現,我們看到它仍然不是很清楚要找什麼內容,因此它應該小。
概括地講,假定一個關鍵詞 w 在 Dw 個網頁中出現過,那麼 Dw 越大,w的權重越小,反之亦然。
在資訊檢索中,使用最多的權重是“逆文字頻率指數” (Inverse document frequency 縮寫為IDF),它的公式為log(D/Dw)其中D是全部網頁數。
比如,我們假定中文網頁數是D=10億,應刪除詞“的”在所有的網頁中都出現,即Dw=10億,那麼它的IDF=log(10億/10億)= log (1) = 0。
假如專用詞“原子能”在兩百萬個網頁中出現,即Dw=200萬,則它的權重IDF=log(500) =2.7。
又假定通用詞“應用”,出現在五億個網頁中,它的權重IDF = log(2)則只有 0.3。
也就是說,在網頁中找到一個“原子能”的匹配相當於找到九個“應用”的匹配。
利用 IDF,上述相關性計算的公式就由詞頻的簡單求和變成了加權求和,即 TF1IDF1 + TF2IDF2 +... + TFN*IDFN。
在上面的例子中,該網頁和“原子能的應用”的相關性為 0.0069,其中“原子能”貢獻了 0.0054,而“應用”只貢獻了0.0015。
這個比例和我們的直覺比較一致了。
詞庫
詞庫與idf
在輸入法中,有詞庫的概念,比如你從事的是IT行業,和同行溝通時,會用到很多IT行業的術語,詞庫的片語可用於切詞。
同一個詞語,在不同行業的詞庫中IDF應該是不一樣的,因為他們的集合不一樣。比如IT行業,總的文件數是100億。醫療行業總的文件數是10億。
“生物”這個詞在這兩個行業中的IDF會一樣嗎?
計算IDF
就像人口普查一樣,IDF的計算也可以做取樣,當然也可以全網計算,特別是搜尋引擎,每天都在爬各種網站的內容,搜尋引擎的內容應該算是最大的。
從搜尋引擎計算出來的IDF相對會比較準確,不過前面也說了,如果做垂直行業的關鍵字提取,需要建立行業自己的IDF,這樣提取的關鍵字準確度可以做到更高。
那麼如何計算IDF呢?實際上很簡單,以資料庫為例。
MADlib TF 統計介面
MADlib是Pivotal與伯克利大學合作的一個開源機器學習庫,可以用在Greenplum, PostgreSQL, HAWQ等資料庫中。
這裡面也有一個TF統計的介面
http://madlib.incubator.apache.org/docs/latest/group__grp__text__utilities.html
內容如下,呼叫term_frequency這個函式可以對儲存文字的表進行統計,得到每條記錄所有WORD的TF
Term frequency tf(t,d) is to the raw frequency of a word/term in a document, i.e. the number of times that word/term t occurs in document d.
For this function, 'word' and 'term' are used interchangeably.
Note: the term frequency is not normalized by the document length.
term_frequency(input_table,
doc_id_col,
word_col,
output_table,
compute_vocab)
引數解釋如下
Arguments:
input_table
TEXT.
The name of the table storing the documents.
Each row is in the form <doc_id, word_vector> where doc_id is an id, unique to each document, and word_vector is a text array containing the words in the document.
The word_vector should contain multiple entries of a word if the document contains multiple occurrence of that word.
id_col
TEXT.
The name of the column containing the document id.
word_col
TEXT.
The name of the column containing the vector of words/terms in the document.
This column should of type that can be cast to TEXT[], 例如tsvector.
output_table
TEXT.
The name of the table to store the term frequency output.
The output table contains the following columns:
id_col:
This the document id column (same as the one provided as input).
word:
A word/term present in a document. This is either the original word present in word_col or an id representing the word (depending on the value of compute_vocab below).
count:
The number of times this word is found in the document.
compute_vocab:
BOOLEAN. (Optional, Default=FALSE) Flag to indicate if a vocabulary is to be created.
If TRUE, an additional output table is created containing the vocabulary of all words, with an id assigned to each word.
The table is called output_table_vocabulary (suffix added to the output_table name) and contains the following columns:
wordid:
An id assignment for each word
word:
The word/term
PostgreSQL 全文統計
PostgreSQL支援全文檢索型別(tsvector),使用ts_stat函式可以對全表統計,word出現的總次數,word出現的文字數。
可以用於提取全表的關鍵詞,也可以用於檢查分詞的有效性,如果發現文字中有大量的stop-word出現,說明分詞效果不好。
https://www.postgresql.org/docs/9.6/static/textsearch-features.html#TEXTSEARCH-STATISTICS
ts_stat函式用法如下
The function ts_stat is useful for checking your configuration and for finding stop-word candidates.
ts_stat(sqlquery text, [ weights text, ]
OUT word text, OUT ndoc integer,
OUT nentry integer) returns setof record
sqlquery is a text value containing an SQL query which must return a single tsvector column.
ts_stat executes the query and returns statistics about each distinct lexeme (word) contained in the tsvector data.
The columns returned are
word text — the value of a lexeme
ndoc integer — number of documents (tsvectors) the word occurred in
nentry integer — total number of occurrences of the word
If weights is supplied, only occurrences having one of those weights are counted.
For example, to find the ten most frequent words in a document collection:
例子
SELECT * FROM ts_stat('SELECT vector FROM apod')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;
The same, but counting only word occurrences with weight A or B:
SELECT * FROM ts_stat('SELECT vector FROM apod', 'ab')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;
例子
postgres=# SELECT * from (values (tsvector 'a b c'),(to_tsvector( 'b c d b'))) t(word);
word
---------------------
'a' 'b' 'c'
'b':1,4 'c':2 'd':3
(2 rows)
postgres=# SELECT * FROM ts_stat($$SELECT * from (values (tsvector 'a b c'),(to_tsvector( 'b c d b'))) t(word)$$);
word | ndoc | nentry
------+------+--------
d | 1 | 1
c | 2 | 2
b | 2 | 3
a | 1 | 1
(4 rows)
在資料庫中如何訓練生成IDF
計算所有詞(包括stop word)的idf
測試表,每條記錄包含一個PK,同時包含一個文字
create table doc(id int primary key, info text);
postgres=# insert into doc values (1,'hi i am digoal');
INSERT 0 1
postgres=# insert into doc values (2,'hi i am abc');
INSERT 0 1
使用對應的分詞(ts_config)配置,對每個文字進行分詞,並計算出word在整表的idf,記錄數越多,IDF越準確(類似文字訓練)
我們還需要用到ts_debug這個函式,它會得到這個詞的token type。
postgres=# select * from ts_debug('a b c hello i am digoal');
alias | description | token | dictionaries | dictionary | lexemes
-----------+-----------------+--------+----------------+--------------+----------
asciiword | Word, all ASCII | a | {english_stem} | english_stem | {}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | b | {english_stem} | english_stem | {b}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | c | {english_stem} | english_stem | {c}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | hello | {english_stem} | english_stem | {hello}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | i | {english_stem} | english_stem | {}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | am | {english_stem} | english_stem | {}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | digoal | {english_stem} | english_stem | {digoal}
(13 rows)
https://www.postgresql.org/docs/9.6/static/textsearch-parsers.html
預設的parser包括如下token type
Alias | Description | Example |
---|---|---|
asciiword | Word, all ASCII letters | elephant |
word | Word, all letters | ma?ana |
numword | Word, letters and digits | beta1 |
asciihword | Hyphenated word, all ASCII | up-to-date |
hword | Hyphenated word, all letters | lógico-matemática |
numhword | Hyphenated word, letters and digits | postgresql-beta1 |
hword_asciipart | Hyphenated word part, all ASCII | postgresql in the context postgresql-beta1 |
hword_part | Hyphenated word part, all letters | lógico or matemática in the context lógico-matemática |
hword_numpart | Hyphenated word part, letters and digits | beta1 in the context postgresql-beta1 |
Email address | foo@example.com | |
protocol | Protocol head | http:// |
url | URL | example.com/stuff/index.html |
host | Host | example.com |
url_path | URL path | /stuff/index.html, in the context of a URL |
file | File or path name | /usr/local/foo.txt, if not within a URL |
sfloat | Scientific notation | -1.234e56 |
float | Decimal notation | -1.234 |
int | Signed integer | -1234 |
uint | Unsigned integer | 1234 |
version | Version number | 8.3.0 |
tag | XML tag | |
entity | XML entity | & |
blank | Space symbols | (any whitespace or punctuation not otherwise recognized) |
統計IDF
with t1 as (
select count(*) as cnt from doc
),
t2 as (
select id, alias, token from
(
select id,(ts_debug(info)).* from doc
) t
group by id, alias, token
)
select t2.token, t2.alias, log(t1.cnt/count(t2.*)) as idf from t1,t2 group by t2.token,t2.alias,t1.cnt;
token | alias | idf
--------+-----------+-------------------
| blank | 0
hi | asciiword | 0
abc | asciiword | 0.301029995663981
am | asciiword | 0
i | asciiword | 0
digoal | asciiword | 0.301029995663981
(6 rows)
使用PostgreSQL提取關鍵詞
如何提取每篇文件的關鍵詞?
1. 計算tf
計算每條記錄(假設每篇文字一條記錄)有多少詞
set default_text_search_config='pg_catalog.english';
select id, length(to_tsvector(info)) as cnt from doc;
計算每篇文件,每個詞出現了多少次
select id, (ts_stat('select to_tsvector(info) from doc where id='||id)).* from doc;
還有一種方法計算tf
https://www.postgresql.org/docs/9.6/static/textsearch-controls.html#TEXTSEARCH-RANKING
ts_rank([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4
Ranks vectors based on the frequency of their matching lexemes.
normalization
0 (the default) ignores the document length
1 divides the rank by 1 + the logarithm of the document length
2 divides the rank by the document length
4 divides the rank by the mean harmonic distance between extents (this is implemented only by ts_rank_cd)
8 divides the rank by the number of unique words in document
16 divides the rank by 1 + the logarithm of the number of unique words in document
32 divides the rank by itself + 1
原始碼
src/backend/utils/adt/tsrank.c
Datum
ts_rank_ttf(PG_FUNCTION_ARGS)
{
TSVector txt = PG_GETARG_TSVECTOR(0);
TSQuery query = PG_GETARG_TSQUERY(1);
int method = PG_GETARG_INT32(2);
float res;
res = calc_rank(getWeights(NULL), txt, query, method);
PG_FREE_IF_COPY(txt, 0);
PG_FREE_IF_COPY(query, 1);
PG_RETURN_FLOAT4(res);
}
Datum
ts_rank_tt(PG_FUNCTION_ARGS)
{
TSVector txt = PG_GETARG_TSVECTOR(0);
TSQuery query = PG_GETARG_TSQUERY(1);
float res;
res = calc_rank(getWeights(NULL), txt, query, DEF_NORM_METHOD);
PG_FREE_IF_COPY(txt, 0);
PG_FREE_IF_COPY(query, 1);
PG_RETURN_FLOAT4(res);
}
static float4
calc_rank_cd(const float4 *arrdata, TSVector txt, TSQuery query, int method)
{
DocRepresentation *doc;
int len,
i,
doclen = 0;
CoverExt ext;
double Wdoc = 0.0;
double invws[lengthof(weights)];
double SumDist = 0.0,
PrevExtPos = 0.0,
CurExtPos = 0.0;
int NExtent = 0;
QueryRepresentation qr;
for (i = 0; i < lengthof(weights); i++)
{
invws[i] = ((double) ((arrdata[i] >= 0) ? arrdata[i] : weights[i]));
if (invws[i] > 1.0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("weight out of range")));
invws[i] = 1.0 / invws[i];
}
qr.query = query;
qr.operandData = (QueryRepresentationOperand *)
palloc0(sizeof(QueryRepresentationOperand) * query->size);
doc = get_docrep(txt, &qr, &doclen);
if (!doc)
{
pfree(qr.operandData);
return 0.0;
}
MemSet(&ext, 0, sizeof(CoverExt));
while (Cover(doc, doclen, &qr, &ext))
{
double Cpos = 0.0;
double InvSum = 0.0;
int nNoise;
DocRepresentation *ptr = ext.begin;
while (ptr <= ext.end)
{
InvSum += invws[WEP_GETWEIGHT(ptr->pos)];
ptr++;
}
Cpos = ((double) (ext.end - ext.begin + 1)) / InvSum;
/*
* if doc are big enough then ext.q may be equal to ext.p due to limit
* of posional information. In this case we approximate number of
* noise word as half cover's length
*/
nNoise = (ext.q - ext.p) - (ext.end - ext.begin);
if (nNoise < 0)
nNoise = (ext.end - ext.begin) / 2;
Wdoc += Cpos / ((double) (1 + nNoise));
CurExtPos = ((double) (ext.q + ext.p)) / 2.0;
if (NExtent > 0 && CurExtPos > PrevExtPos /* prevent devision by
* zero in a case of
multiple lexize */ )
SumDist += 1.0 / (CurExtPos - PrevExtPos);
PrevExtPos = CurExtPos;
NExtent++;
}
if ((method & RANK_NORM_LOGLENGTH) && txt->size > 0)
Wdoc /= log((double) (cnt_length(txt) + 1));
if (method & RANK_NORM_LENGTH)
{
len = cnt_length(txt);
if (len > 0)
Wdoc /= (double) len;
}
if ((method & RANK_NORM_EXTDIST) && NExtent > 0 && SumDist > 0)
Wdoc /= ((double) NExtent) / SumDist;
if ((method & RANK_NORM_UNIQ) && txt->size > 0)
Wdoc /= (double) (txt->size);
if ((method & RANK_NORM_LOGUNIQ) && txt->size > 0)
Wdoc /= log((double) (txt->size + 1)) / log(2.0);
if (method & RANK_NORM_RDIVRPLUS1)
Wdoc /= (Wdoc + 1);
pfree(doc);
pfree(qr.operandData);
return (float4) Wdoc;
}
2. 計算idf
with t1 as (
select count(*) as cnt from doc
),
t2 as (
select id, alias, token from
(
select id,(ts_debug(info)).* from doc
) t
group by id, alias, token
)
select t2.token, t2.alias, log(t1.cnt/count(t2.*)) as idf from t1,t2 group by t2.token,t2.alias,t1.cnt;
3. 計算每個詞的tf-idf
tf * idf
4. 將以上邏輯寫成函式即可提取tf*idf值的TOPN詞即文字的關鍵詞
參考
http://baike.baidu.com/view/1228847.htm
相關文章
- [精選] 用PHP做圖片防盜鏈,你再也盜不了圖片了?PHP
- HttpHandler應用之 - 防止圖片盜鏈HTTP
- 盜墓筆記筆記
- 盜墓中的坑
- 盜墓筆記2筆記
- Nginx中防盜鏈(下載防盜鏈和圖片防盜鏈)及圖片訪問地址操作記錄Nginx
- c# 圖片防盜鏈C#
- 微信文章圖片防盜鏈處理方法
- 《盜墓筆記》從歐美轉戰港澳臺!盜墓題材會成為新的引爆點嗎?筆記
- 《盜墓筆記》IP使用者畫像——資訊圖筆記
- 愛奇藝、樂視互撕:盜播盜墓筆記、羋月傳筆記
- TF-IDF與餘弦相似性的應用(二):找出相似文章
- 為Nginx配置圖片防盜鏈薦Nginx
- 隆重推薦:盜墓筆記1-9部筆記
- Vue專案圖片加密處理,防止直接在網頁上盜圖。Vue加密網頁
- TF-IDF與餘弦相似性的應用(一):自動提取關鍵詞
- TF-IDF與餘弦相似性的應用(三):自動摘要
- 中關村推介防盜版平臺 可從根本上杜絕盜版中關村
- 如何保護在Autodesk應用程式商店的應用不被盜版 - 1
- 文字相似度計算之餘弦定理
- 微信圖片防盜鏈解決方案:自建代理繞過限制。
- 圖論連通性相關圖論
- 【盜墓筆記】電影版,鹿晗主演下載資源筆記
- 建立海盜的天堂:盜賊之海的AI(一)AI
- 聽說你的資源被盜用了,那你知道 Nginx 怎麼防盜鏈嗎?Nginx
- 盜文高手(DownFiles) Ver1.3 註冊演算法演算法
- 線性dp:大盜阿福(打家劫舍)
- 盜號原理
- [php]referer應用--http防盜鏈技術PHPHTTP
- 海盜分贓演算法題演算法
- 中國人不願給影片付錢?《盜墓筆記》逆轉筆記
- 【建站教程】網站引用三方圖片遇到簡單防盜鏈referer的處理辦法網站
- 安卓現盜號木馬威脅網銀盜刷安卓
- 【盜墓筆記】圖解使用fat-aar方式在AndroidStudio中打包巢狀第三方aar的aar筆記圖解Android巢狀
- 在傳統軟體公司十年深惡痛絕的感受
- 一文搞定防盜鏈設計
- PostgreSQL技術週刊第2期:用PostgreSQL解海盜分金問題SQL
- Nginx 防盜鏈Nginx