關鍵字提取屬於無監督資訊檢索任務,核心假設:文檔中出現頻率高、分布集中、長度適中的詞語更可能代表主題。本文實作基於字元級 n-gram + 詞頻統計 + 重疊過濾的輕量演算法,無需分詞詞典,適合中文短文本 (標題、摘要、評論)。
minLen ~ maxLen,統計各 n-gram 出現次數。minFreq 的候選。<?php
/**
* 關鍵字提取器
*/
class KeywordExtractor {
public function __construct(
private int $minLen = 2,
private int $maxLen = 10,
private int $minFreq = 2,
private int $topK = 20
) {}
public function extract(string $text): array {
$len = mb_strlen($text, 'UTF-8');
if ($len < $this->minLen) return [];
// 1. 統計所有 n-gram 詞頻
$freq = [];
for ($l = $this->minLen; $l <= $this->maxLen; $l++) {
for ($i = 0; $i <= $len - $l; $i++) {
$gram = mb_substr($text, $i, $l, 'UTF-8');
// 過濾純標點/數字/空白
if (!preg_match('/^[\p{L}\p{N}]+$/u', $gram)) continue;
$freq[$gram] = ($freq[$gram] ?? 0) + 1;
}
}
// 2. 詞頻門檻過濾
$candidates = array_filter($freq, fn($c) => $c >= $this->minFreq);
if (empty($candidates)) return [];
// 3. 按詞頻降序、長度降序排序
uasort($candidates, function($a, $b) {
if ($a !== $b) return $b - $a; // 詞頻高優先
return mb_strlen($b, 'UTF-8') - mb_strlen($a, 'UTF-8'); // 長詞優先
});
// 4. 重疊過濾:若候選 A 包含 B,且 freq(A) >= freq(B),捨棄 B
$result = [];
foreach ($candidates as $gram => $count) {
$covered = false;
foreach ($result as $existing) {
if (mb_strpos($existing, $gram, 0, 'UTF-8') !== false) {
$covered = true;
break;
}
}
if (!$covered) {
$result[$gram] = $count;
if (count($result) >= $this->topK) break;
}
}
return array_keys($result);
}
}
// 使用範例
$text = "PHP 是一種流行的伺服器端腳本語言,PHP 適用於 Web 開發,PHP 可以嵌入 HTML 中。";
$extractor = new KeywordExtractor(minLen: 2, maxLen: 6, minFreq: 2, topK: 10);
$keywords = $extractor->extract($text);
print_r($keywords);
/*
Array
(
[0] => PHP
[1] => 語言
[2] => 開發
[3] => 伺服器
[4] => 腳本
[5] => 嵌入
[6] => HTML
)
*/
| 策略 | 說明 | 效果 |
|---|---|---|
| TF-IDF 加權 | 引入語料庫 IDF,抑制通用詞 (「的」、「是」、「在」) | 大幅提升準確率 |
| TextRank / PageRank | 建立詞共現圖,迭代計算重要度 | 捕捉語義關聯,非單純詞頻 |
| 結構化特徵 | 標題、H1、Strong、Meta Keywords 權重加成 | 利用 HTML 結構資訊 |
| 停用詞表 | 預先過濾高頻無意義詞 | 減少雜訊、加速計算 |
| 分詞器整合 (jieba-php) | 詞級 n-gram 替代字元級 | 語義邊界更精準 |