2009-05-12 41 views
19

Hãy tưởng tượng vấn đề sau đây:Thuật toán phân cụm tốt nhất? (Chỉ cần giải thích)

  • Bạn có một cơ sở dữ liệu chứa khoảng 20.000 văn bản trong một bảng gọi là "bài báo"
  • Bạn muốn kết nối những người có liên quan đến sử dụng một thuật toán phân nhóm để hiển thị bài viết liên quan cùng
  • thuật toán nên làm clustering phẳng (không phân cấp)
  • các bài viết liên quan nên được chèn vào bảng "liên quan"
  • thuật toán phân nhóm nên quyết định xem hai hoặc mor bài báo điện tử có liên quan hay không dựa trên các văn bản
  • Tôi muốn viết mã trong PHP nhưng ví dụ với mã giả hoặc các ngôn ngữ lập trình khác là ok, quá

Tôi đã mã hóa một dự thảo đầu tiên với một tấm séc function () cung cấp cho "true" nếu hai bài viết đầu vào có liên quan và "sai" nếu không. Phần còn lại của mã (chọn các bài báo từ cơ sở dữ liệu, chọn bài viết để so sánh, chèn các bài liên quan) cũng đã hoàn thành. Có lẽ bạn cũng có thể cải thiện phần còn lại. Nhưng điểm chính quan trọng đối với tôi là kiểm tra hàm(). Vì vậy, nó sẽ là tuyệt vời nếu bạn có thể đăng một số cải tiến hoặc phương pháp tiếp cận hoàn toàn khác nhau.

TIẾP CẬN 1

<?php 
$zeit = time(); 
function check($str1, $str2){ 
    $minprozent = 60; 
    similar_text($str1, $str2, $prozent); 
    $prozent = sprintf("%01.2f", $prozent); 
    if ($prozent > $minprozent) { 
     return TRUE; 
    } 
    else { 
     return FALSE; 
    } 
} 
$sql1 = "SELECT id, text FROM articles ORDER BY RAND() LIMIT 0, 20"; 
$sql2 = mysql_query($sql1); 
while ($sql3 = mysql_fetch_assoc($sql2)) { 
    $rel1 = "SELECT id, text, MATCH (text) AGAINST ('".$sql3['text']."') AS score FROM articles WHERE MATCH (text) AGAINST ('".$sql3['text']."') AND id NOT LIKE ".$sql3['id']." LIMIT 0, 20"; 
    $rel2 = mysql_query($rel1); 
    $rel2a = mysql_num_rows($rel2); 
    if ($rel2a > 0) { 
     while ($rel3 = mysql_fetch_assoc($rel2)) { 
      if (check($sql3['text'], $rel3['text']) == TRUE) { 
       $id_a = $sql3['id']; 
       $id_b = $rel3['id']; 
       $rein1 = "INSERT INTO related (article1, article2) VALUES ('".$id_a."', '".$id_b."')"; 
       $rein2 = mysql_query($rein1); 
       $rein3 = "INSERT INTO related (article1, article2) VALUES ('".$id_b."', '".$id_a."')"; 
       $rein4 = mysql_query($rein3); 
      } 
     } 
    } 
} 
?> 

TIẾP CẬN 2 [chỉ kiểm tra()]

<?php 
function square($number) { 
    $square = pow($number, 2); 
    return $square; 
} 
function check($text1, $text2) { 
    $words_sub = text_splitter($text2); // splits the text into single words 
    $words = text_splitter($text1); // splits the text into single words 
    // document 1 start 
    $document1 = array(); 
    foreach ($words as $word) { 
     if (in_array($word, $words)) { 
      if (isset($document1[$word])) { $document1[$word]++; } else { $document1[$word] = 1; } 
     } 
    } 
    $rating1 = 0; 
    foreach ($document1 as $temp) { 
     $rating1 = $rating1+square($temp); 
    } 
    $rating1 = sqrt($rating1); 
    // document 1 end 
    // document 2 start 
    $document2 = array(); 
    foreach ($words_sub as $word_sub) { 
     if (in_array($word_sub, $words)) { 
      if (isset($document2[$word_sub])) { $document2[$word_sub]++; } else { $document2[$word_sub] = 1; } 
     } 
    } 
    $rating2 = 0; 
    foreach ($document2 as $temp) { 
     $rating2 = $rating2+square($temp); 
    } 
    $rating2 = sqrt($rating2); 
    // document 2 end 
    $skalarprodukt = 0; 
    for ($m=0; $m<count($words)-1; $m++) { 
     $skalarprodukt = $skalarprodukt+(array_shift($document1)*array_shift($document2)); 
    } 
    if (($rating1*$rating2) == 0) { continue; } 
    $kosinusmass = $skalarprodukt/($rating1*$rating2); 
    if ($kosinusmass < 0.7) { 
     return FALSE; 
    } 
    else { 
     return TRUE; 
    } 
} 
?> 

Tôi cũng muốn nói rằng tôi biết rằng có rất nhiều thuật toán cho clustering nhưng trên mọi trang web chỉ có mô tả toán học hơi khó hiểu đối với tôi. Vì vậy, các ví dụ mã hóa trong (giả) mã sẽ là tuyệt vời.

Tôi hy vọng bạn có thể giúp tôi. Cảm ơn trước!

+1

Có các plugin WordPress (có, yuck, tôi biết, tha cho tôi) làm một công việc đáng kinh ngạc ở đây, họ thực sự thực hiện phân cụm hợp lý (thường là TF-IDF với các từ bằng k-means hoặc cái gì đó như thế) và bạn có thể sử dụng chúng để truyền cảm hứng (một số trong số đó là nguồn mở theo MIT). –

+2

Tôi nghĩ Anony-Mousse là đúng: phân cụm không phải là công cụ lý tưởng ở đây. Nếu mỗi tài liệu thuộc về 1 cụm, thì bạn có vấn đề về tài liệu gần ranh giới của cụm đang * tương tự hơn * đối với tài liệu trong các cụm lân cận khác so với hầu hết các tài liệu trong cụm của riêng chúng. –

Trả lời

15

Cách tiêu chuẩn nhất mà tôi biết để thực hiện điều này trên dữ liệu văn bản như bạn có, là sử dụng kỹ thuật 'túi từ'.

Trước tiên, hãy tạo 'biểu đồ' các từ cho mỗi bài viết. Cho phép nói giữa tất cả các bài viết của bạn, bạn chỉ có 500 từ duy nhất giữa chúng. Sau đó, biểu đồ này sẽ là một vectơ (Array, List, Whatever) có kích thước 500, trong đó dữ liệu là số lần mỗi từ xuất hiện trong bài báo. Vì vậy, nếu vị trí đầu tiên trong vector đại diện từ 'hỏi', và từ đó xuất hiện 5 lần trong bài viết, vector [0] sẽ là 5:

for word in article.text 
    article.histogram[indexLookup[word]]++ 

Bây giờ, để so sánh bất kỳ hai bài báo, nó là khá đơn giản. Chúng tôi chỉ đơn giản là nhân hai vectơ:

def check(articleA, articleB) 
    rtn = 0 
    for a,b in zip(articleA.histogram, articleB.histogram) 
     rtn += a*b 
    return rtn > threshold 

(Xin lỗi vì sử dụng python thay vì PHP, PHP của tôi là Rusty và việc sử dụng zip làm cho rằng chút dễ dàng hơn)

Đây là ý tưởng cơ bản. Lưu ý giá trị ngưỡng là bán tùy ý; có thể bạn sẽ muốn tìm một cách tốt để bình thường hóa sản phẩm chấm của biểu đồ của bạn (điều này gần như sẽ có yếu tố trong chiều dài bài viết ở đâu đó) và quyết định những gì bạn xem là 'liên quan'.

Ngoài ra, bạn không nên đặt mọi từ vào biểu đồ của mình. Bạn sẽ, nói chung, muốn bao gồm những cái được sử dụng bán thường xuyên: Không phải trong mỗi bài viết cũng không chỉ trong một bài viết. Điều này giúp bạn tiết kiệm một chút chi phí trên biểu đồ của bạn và tăng giá trị của các mối quan hệ của bạn.

Bằng cách này, kỹ thuật này được mô tả chi tiết here

+0

Cảm ơn bạn rất nhiều! Tôi đã cố gắng viết mã cho cách tiếp cận của bạn bằng PHP và đây là kết quả: http://paste.bradleygill.com/index.php?paste_id=9290 Tôi hy vọng PHP của bạn vẫn đủ tốt để nói nếu nó đúng hay không. – caw

+1

Dường như với tôi là chính xác, tuy nhiên, tùy thuộc vào ứng dụng của bạn, bạn nghiêm túc muốn xem xét sự bền bỉ trạng thái của các vectơ hạn. Ngoài ra, hãy xem xét chia số điểm theo độ dài của bài viết một lần độ dài của bài viết b. Nếu không, bạn sẽ thấy sự thiên vị đối với các bài viết dài chỉ có liên quan một phần. – Albinofrenchy

+0

Xin lỗi, chắc chắn là một câu hỏi ngu ngốc, nhưng điều gì làm bạn có ý nghĩa chính xác với "xem xét sự bền bỉ trạng thái của các vectơ hạn." Ở điểm thứ hai: Bạn có nghĩa là "$ score = $ score/$ length_a * $ length_b" hay "$ score = $ score/($ length_a * $ length_b)"? Có lẽ là người đầu tiên, phải không? – caw

0

similar_text chức năng gọi trong cách tiếp cận # 1 cái nhìn như thế nào? Tôi nghĩ rằng những gì bạn đang đề cập đến không phải là phân nhóm, nhưng một số liệu tương tự. Tôi thực sự không thể cải thiện cách tiếp cận biểu đồ :-) của White Walloun - một vấn đề thú vị để làm một số việc đọc.

Tuy nhiên, bạn thực hiện check(), bạn phải sử dụng nó để thực hiện so sánh tối thiểu 200 triệu (một nửa số 20000^2). Các cắt các bài báo "có liên quan" có thể hạn chế những gì bạn lưu trữ trong cơ sở dữ liệu, nhưng dường như quá độc đoán để đón tất cả phân nhóm hữu ích của văn bản,

cách tiếp cận của tôi sẽ được sửa đổi check() để trả lại "tương đồng" metric ($prozent hoặc rtn). Viết ma trận 20K x 20K vào một tệp và sử dụng chương trình bên ngoài để thực hiện phân cụm để xác định các hàng xóm gần nhất cho mỗi bài viết mà bạn có thể tải vào bảng related. Tôi sẽ thực hiện phân cụm theo số R - có một số tutorial để phân cụm dữ liệu trong tệp đang chạy R từ php.

+0

Hàm same_text() "tính toán sự giống nhau giữa hai chuỗi như được mô tả trong Oliver [1993]". Có, bạn nói đúng, đó là một số liệu tương tự. Nhưng bạn cần kiểm tra tương tự để phân cụm, phải không? – caw

1

Tôi tin rằng bạn cần phải thực hiện một số quyết định thiết kế về clustering, và tiếp tục từ đó:

  1. Tại sao các bạn gom cụm văn bản? Bạn có muốn hiển thị các tài liệu liên quan cùng nhau không? Bạn có muốn khám phá kho dữ liệu tài liệu của mình thông qua các cụm không?
  2. Do đó, bạn có muốn flat hoặc hierarchical phân cụm không?
  3. Bây giờ chúng tôi có vấn đề phức tạp, theo hai chiều: đầu tiên, số lượng và loại đối tượng địa lý bạn tạo từ văn bản - các từ riêng lẻ có thể đánh số trong hàng chục nghìn. Bạn có thể muốn thử một số feature selection - chẳng hạn như lấy N từ thông tin nhất, hoặc N từ xuất hiện nhiều nhất, sau khi bỏ qua stop words.
  4. Thứ hai, bạn muốn giảm thiểu số lần bạn đo độ tương tự giữa các tài liệu. Như bubaker chỉ ra một cách chính xác, kiểm tra sự giống nhau giữa tất cả các cặp tài liệu có thể là quá nhiều. Nếu phân cụm thành một số ít cụm là đủ, bạn có thể xem xét K-means clustering, về cơ bản: chọn một tài liệu K ban đầu làm cụm trung tâm, gán mọi tài liệu cho cụm gần nhất, tính lại các trung tâm cụm bằng cách tìm phương tiện vectơ tài liệu và lặp lại. Điều này chỉ tốn K * số lượng tài liệu cho mỗi lần lặp lại. Tôi tin rằng cũng có các phỏng đoán để giảm số lượng tính toán cần thiết cho phân cụm theo cấp bậc.
+0

Cảm ơn, câu hỏi hay! 1) Tôi muốn hiển thị các tài liệu liên quan cùng nhau. 2) Thuật toán nên làm phân cụm bằng phẳng. 3) Điều này sẽ hữu ích nếu các văn bản dài, nhưng trong trường hợp của tôi, các bài viết chứa tối đa 510 ký tự. Vì vậy, nó không thực sự cần thiết, phải không? 4) Cách tiếp cận với phương tiện k có vẻ tốt nhưng tôi cần nhiều cụm và cụm mới phải được tạo liên tục. Tôi có thể dùng k-means được không? – caw

+0

Bạn có thể sử dụng K-means với K là rất lớn. Chi phí là phải kiểm tra sự giống nhau của từng tài liệu với mỗi trung tâm của cụm. 'Liên tục tạo âm thanh cụm mới như phân cụm từ trên xuống với tôi, nhưng bạn có thể làm việc trong nhiều kỷ nguyên - bắt đầu bằng chữ K nhỏ, chạy K-means cho đến khi nó hội tụ và sử dụng các cụm này. Sau đó, tăng K, chạy lại K-means ngay từ đầu và sử dụng các cụm kết quả, v.v. –

+0

Ồ, tôi không biết cách hoạt động chính xác của k.Nếu nó hoạt động như thế, tôi không thể sử dụng nó vì tôi không biết số lượng trung tâm cụm. Tôi có một cơ sở dữ liệu các bài báo và tất cả các bài viết về cùng một chủ đề nên được nhóm lại. – caw

5

Có thể phân nhóm là chiến lược sai ở đây?

Nếu bạn muốn hiển thị tương tự bài viết, sử dụng tương đồng tìm kiếm thay.

Đối với các bài viết văn bản, điều này được hiểu rõ. Chỉ cần chèn các bài viết của bạn vào cơ sở dữ liệu tìm kiếm văn bản như Lucene và sử dụng bài viết hiện tại của bạn làm truy vấn tìm kiếm.Trong Lucene, có một query called MoreLikeThis thực hiện chính xác điều này: tìm các bài viết tương tự.

Cụm là công cụ sai, bởi vì (đặc biệt với yêu cầu của bạn), mỗi bài viết phải được đưa vào một số cụm; và các mục có liên quan sẽ giống nhau đối với mọi đối tượng trong cụm. Nếu có các ngoại lệ trong cơ sở dữ liệu - một trường hợp rất có khả năng - chúng có thể làm hỏng cụm của bạn. Hơn nữa, các cụm có thể rất lớn. Không có ràng buộc về kích thước, thuật toán phân cụm có thể quyết định đặt một nửa bộ dữ liệu của bạn vào cùng một cụm. Vì vậy, bạn có 10000 bài viết liên quan cho mỗi bài viết trong cơ sở dữ liệu của bạn. Với tìm kiếm tương tự, bạn chỉ có thể nhận được 10 mục tương tự hàng đầu cho mỗi tài liệu!

Cuối cùng nhưng không kém phần quan trọng: quên PHP để phân cụm. Nó không được thiết kế cho điều này, và không thực hiện đủ. Nhưng bạn có lẽ có thể truy cập một chỉ số lucene từ PHP cũng đủ.

Các vấn đề liên quan