【自然語言處理】深度學習中文本分類實現

文本分類是NLP中最基礎也是應用最廣泛的任務之一,從無用的郵件過濾到情感分析,從新聞分類到智能客服,都離不開高效準確的文本分類技術。本文將帶您全面了解文本分類的技術演進,從傳統機器學習到深度學習,手把手實現一套完整的新聞分類系統!

文本分類基礎理論

文本分類(Text Classification)是自然語言處理(NLP)中的基礎任務,簡單來說就是把文本按照預定義的類別進行歸類。表面上看挺簡單,但實際操作中的坑卻不少。

文本分類任務定義與應用場景

從數學角度來看,文本分類的定義是這樣的:

給定文檔集 D = { d 1 , d 2 , . . . , d n } D = \{d_1, d_2, ..., d_n\} D={d1?,d2?,...,dn?} 和類別集 C = { c 1 , c 2 , . . . , c m } C = \{c_1, c_2, ..., c_m\} C={c1?,c2?,...,cm?},文本分類就是要找到一個映射函數 f : D → C f: D \rightarrow C f:DC,讓每個文檔 d i d_i di? 都能正確地對應到它的類別 c j c_j cj?

文本分類在我們日常生活中隨處可見,舉幾個例子:

  • 情感分析:判斷一條評論是正面、負面還是中性的
  • 無用的信息過濾:識別并攔截無用的郵件、無用的評論(沒有這個功能我的郵箱早就爆炸了)
  • 新聞分類:自動把新聞分到政治、經濟、體育等欄目(我認識的一位記者朋友說這功能為他們節省了不少工作量)
  • 輿情監測:監控社交媒體上的熱點話題和負面輿情
  • 客服自動回復:根據用戶提問自動分類并給出答復(雖然有時候答非所問挺讓人抓狂的)
  • 內容推薦:基于你的興趣給你推薦相關內容(這就是為什么你看了一個游戲視頻后,推薦欄突然全變成游戲了)

在這里插入圖片描述

監督學習與非監督學習方法

文本分類主要有兩種學習方式:

1. 監督學習

  • 需要有標注好的訓練數據(標注過程真的很費人力)
  • 通過學習文本和標簽之間的對應關系來進行分類
  • 常用算法包括樸素貝葉斯、SVM和各種深度神經網絡等
  • 優點:準確率高,解釋性好
  • 缺點:需要大量標注數據,而且標注成本高(一個團隊可能需要花費兩周時間進行數據標注)

2. 非監督學習

  • 不需要標注數據,直接從文本自身特征進行聚類
  • 主要用于話題發現、文檔聚類等任務
  • 常用算法有K-Means、層次聚類、LDA主題模型等
  • 優點:不用標注數據,省事省錢
  • 缺點:精度一般,而且聚類結果不一定符合預期

在實際工作中,還有半監督學習、弱監督學習和遷移學習等混合方法,可以在標注數據有限的情況下提升分類效果。曾經有一個項目只給了500條標注數據,通過半監督學習將性能提升了7個百分點。

評估指標與性能分析

評估文本分類模型不能只看準確率,特別是當數據不平衡時,準確率這個指標就很容易產生誤導了。

基礎指標

  • 準確率(Accuracy):預測對的樣本數 / 總樣本數
  • 精確率(Precision):真正例 / (真正例 + 假正例),也就是預測為正的樣本中有多少是真正的正樣本
  • 召回率(Recall):真正例 / (真正例 + 假負例),也就是所有真正的正樣本中有多少被成功預測出來了
  • F1值:精確率和召回率的調和平均, F 1 = 2 ? P r e c i s i o n ? R e c a l l P r e c i s i o n + R e c a l l F1 = 2 \cdot \frac{Precision \cdot Recall}{Precision + Recall} F1=2?Precision+RecallPrecision?Recall?

多分類問題指標

  • 宏平均(Macro-average):先算出每個類別的指標,再求平均(給每個類別同等權重)
  • 微平均(Micro-average):先合并所有類別的混淆矩陣,再計算整體指標
  • 加權平均(Weighted-average):按各類別樣本數加權平均(樣本多的類別權重大)

下面是個多分類性能報告的例子:

類別精確率召回率F1值支持度
體育0.950.970.96500
政治0.870.820.84450
科技0.900.920.91480
文化0.850.810.83400
微平均0.900.890.891830
宏平均0.890.880.891830
加權平均0.900.890.891830

在實際項目中,我們需要根據業務需求確定重點關注哪些指標。比如無用的郵件過濾,你更在乎精確率(別把正常郵件當成無用的郵件了);而在欺詐檢測中,你可能更看重召回率(寧可錯殺一千,不可放過一個)。

文本特征表示方法概覽

文本不能直接喂給機器學習模型,得先轉成數值特征。常見的幾種表示方法有:

1. 詞袋模型(Bag of Words, BoW)

  • 把文檔表示成詞頻向量
  • 完全不考慮詞序和語法,只關心詞出現了多少次
  • 向量維度等于詞表大小(可想而知會很大)
  • 優點:簡單直接,容易理解和實現
  • 缺點:維度高、稀疏,而且沒有語義信息("好吃"和"難吃"在向量空間中距離很近)

2. TF-IDF(詞頻-逆文檔頻率)

  • TF表示詞在文檔中的頻率
  • IDF表示詞在整個語料庫中的稀有程度
  • TF-IDF = TF * IDF (常見詞的權重會被降低)
  • 優點:考慮了詞的重要性,比純詞頻更合理
  • 缺點:還是高維稀疏向量,語義表達能力有限

3. 詞嵌入(Word Embeddings)

  • 把詞映射到低維稠密向量空間(通常幾百維)
  • 常用算法:Word2Vec, GloVe, FastText
  • 一般用文檔中所有詞向量的平均值作為文檔向量
  • 優點:低維、稠密、包含語義(相似詞的向量相似)
  • 缺點:簡單平均會忽略詞序,造成信息損失

4. 文檔嵌入(Document Embeddings)

  • 直接學習整個文檔的向量表示
  • 代表方法:Doc2Vec, BERT等預訓練模型的[CLS]向量
  • 優點:能捕獲整個文檔的語義
  • 缺點:計算成本高,訓練麻煩

在這里插入圖片描述

選擇合適的特征表示方法對分類效果影響很大。在一個項目中研究人員試了好幾種特征表示方法,用TF-IDF的準確率比詞袋模型高了5個百分點,換成BERT后又提高了7個百分點。因此,通常會嘗試多種特征表示方法,通過交叉驗證選擇最合適的。

了解完文本分類的基礎理論,接下來深入各種分類算法,從傳統機器學習到深度學習,一個個來看看它們的原理和優缺點。

傳統機器學習分類器

別被現在深度學習的熱度迷惑,在很多實際場景中,傳統機器學習方法依然很給力。特別是在數據量不大、計算資源有限的情況下,這些"老兵"往往能用更少的成本達到不錯的效果。

樸素貝葉斯模型原理與實現

樸素貝葉斯可能是最經典的文本分類算法了。它基于貝葉斯定理,同時假設特征之間相互獨立(雖然這個假設在現實中基本不成立,但它就是莫名其妙地好用)。

原理解析

貝葉斯定理:

P ( c ∣ d ) = P ( d ∣ c ) ? P ( c ) P ( d ) P(c|d) = \frac{P(d|c) \cdot P(c)}{P(d)} P(cd)=P(d)P(dc)?P(c)?

這里:

  • P ( c ∣ d ) P(c|d) P(cd):文檔 d d d屬于類別 c c c的后驗概率(這是我們想要的)
  • P ( d ∣ c ) P(d|c) P(dc):文檔 d d d在類別 c c c中出現的似然概率
  • P ( c ) P(c) P(c):類別 c c c的先驗概率(就是訓練集中這個類別的占比)
  • P ( d ) P(d) P(d):文檔 d d d的概率(可以忽略,因為對所有類別都一樣)

樸素貝葉斯的"樸素"就體現在它假設文檔中的單詞都相互獨立,所以:

P ( d ∣ c ) = P ( w 1 , w 2 , . . . , w n ∣ c ) = ∏ i = 1 n P ( w i ∣ c ) P(d|c) = P(w_1, w_2, ..., w_n|c) = \prod_{i=1}^{n} P(w_i|c) P(dc)=P(w1?,w2?,...,wn?c)=i=1n?P(wi?c)

分類時,選擇讓 P ( c ∣ d ) P(c|d) P(cd)最大的類別:

c ? = arg ? max ? c P ( c ∣ d ) = arg ? max ? c P ( c ) ∏ i = 1 n P ( w i ∣ c ) c^* = \arg \max_c P(c|d) = \arg \max_c P(c) \prod_{i=1}^{n} P(w_i|c) c?=argcmax?P(cd)=argcmax?P(c)i=1n?P(wi?c)

樸素貝葉斯的三種變體

  1. 多項式樸素貝葉斯(Multinomial NB):考慮詞頻,適合文本分類
  2. 伯努利樸素貝葉斯(Bernoulli NB):只考慮詞是否出現,不管出現幾次
  3. 高斯樸素貝葉斯(Gaussian NB):適用于連續特征,假設服從高斯分布

實現代碼

from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.pipeline import Pipeline# 創建樸素貝葉斯分類器流水線
nb_pipeline = Pipeline([('vectorizer', CountVectorizer(max_features=10000)),('classifier', MultinomialNB(alpha=1.0))  # alpha為平滑參數
])# 訓練模型
nb_pipeline.fit(X_train, y_train)# 預測
y_pred = nb_pipeline.predict(X_test)# 評估
accuracy = accuracy_score(y_test, y_pred)
print(f"樸素貝葉斯準確率: {accuracy:.4f}")

優缺點分析

優點:

  • 訓練和預測速度特別快,能處理幾十萬條文本,幾乎是秒出結果
  • 對小數據集效果出奇地好,有時候幾百條樣本就能訓練出不錯的模型
  • 可解釋性強,能清楚地知道是哪些詞對分類起了決定性作用
  • 對不相關特征不太敏感

缺點:

  • 特征獨立性假設太理想化了,實際中詞語之間明顯存在關聯
  • 對數據分布比較敏感
  • 遇到訓練集沒見過的特征就容易出錯
  • 隨著特征維度變化,性能表現不夠穩定

實用避坑技巧

  1. 特征選擇:用卡方檢驗之類的方法挑選最相關的特征,降維增效
  2. 平滑處理:一定要用拉普拉斯平滑(Laplace smoothing)解決零概率問題
  3. 類別不平衡:調整先驗概率或用重采樣(不然小類別容易被忽略)
  4. 參數調優:alpha參數(拉普拉斯平滑參數)需要多試幾個值
  5. 對數空間計算:實際代碼中用對數防止數值下溢(連乘很容易變成0)

支持向量機在文本分類中的應用

支持向量機(Support Vector Machine, SVM)是另一個在文本分類中表現出色的傳統算法,特別適合處理高維稀疏的文本特征(就是詞袋模型和TF-IDF那種)。

原理解析

SVM的核心思想是找一個超平面,讓不同類別的樣本之間的間隔(margin)最大化。對于線性可分的情況,SVM的優化目標是:

min ? w , b 1 2 ∣ ∣ w ∣ ∣ 2 \min_{w, b} \frac{1}{2} ||w||^2 w,bmin?21?∣∣w2
s . t . y i ( w T x i + b ) ≥ 1 , i = 1 , 2 , . . . , n s.t. \quad y_i(w^T x_i + b) \geq 1, \quad i=1,2,...,n s.t.yi?(wTxi?+b)1,i=1,2,...,n

對于線性不可分的情況,引入軟間隔(soft margin)和核函數(kernel function):

min ? w , b , ξ 1 2 ∣ ∣ w ∣ ∣ 2 + C ∑ i = 1 n ξ i \min_{w, b, \xi} \frac{1}{2} ||w||^2 + C \sum_{i=1}^{n} \xi_i w,b,ξmin?21?∣∣w2+Ci=1n?ξi?
s . t . y i ( w T ? ( x i ) + b ) ≥ 1 ? ξ i , ξ i ≥ 0 , i = 1 , 2 , . . . , n s.t. \quad y_i(w^T \phi(x_i) + b) \geq 1 - \xi_i, \quad \xi_i \geq 0, \quad i=1,2,...,n s.t.yi?(wT?(xi?)+b)1?ξi?,ξi?0,i=1,2,...,n

其中 ? ( x ) \phi(x) ?(x)是特征映射函數,通過核函數 K ( x i , x j ) = ? ( x i ) T ? ( x j ) K(x_i, x_j) = \phi(x_i)^T \phi(x_j) K(xi?,xj?)=?(xi?)T?(xj?)隱式定義。

文本分類中的SVM實現

from sklearn.svm import LinearSVC
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import Pipeline
from sklearn.calibration import CalibratedClassifierCV  # 用于獲取概率輸出# 創建SVM分類器流水線
svm_pipeline = Pipeline([('vectorizer', TfidfVectorizer(max_features=10000)),('classifier', CalibratedClassifierCV(LinearSVC(C=1.0, dual=False)))
])# 訓練模型
svm_pipeline.fit(X_train, y_train)# 預測
y_pred = svm_pipeline.predict(X_test)
y_prob = svm_pipeline.predict_proba(X_test)  # 概率輸出# 評估
accuracy = accuracy_score(y_test, y_pred)
print(f"SVM準確率: {accuracy:.4f}")

核函數選擇

文本分類常用的核函數有:

  1. 線性核(Linear) K ( x i , x j ) = x i T x j K(x_i, x_j) = x_i^T x_j K(xi?,xj?)=xiT?xj?

    • 文本分類首選這個,因為高維文本特征通常線性可分
    • 計算效率高,幾乎都可以用這個,又快又好
  2. 多項式核(Polynomial) K ( x i , x j ) = ( γ x i T x j + r ) d K(x_i, x_j) = (\gamma x_i^T x_j + r)^d K(xi?,xj?)=(γxiT?xj?+r)d

    • 理論上可以捕捉詞組合關系
    • 但調參特別麻煩,而且計算速度慢,一般不推薦
  3. RBF核(Radial Basis Function) K ( x i , x j ) = exp ? ( ? γ ∣ ∣ x i ? x j ∣ ∣ 2 ) K(x_i, x_j) = \exp(-\gamma ||x_i - x_j||^2) K(xi?,xj?)=exp(?γ∣∣xi??xj?2)

    • 適合非線性關系
    • 但在文本分類中表現通常不如線性核,反而會浪費計算資源

在這里插入圖片描述

優缺點分析

優點:

  • 在高維空間效果特別好,天生適合文本數據
  • 對內存友好(只用支持向量,不用全部樣本)
  • 特征數量大于樣本數時也能發揮作用
  • 理論基礎扎實,泛化能力強

缺點:

  • 訓練速度慢,大規模數據集上實在讓人抓狂(百萬級數據可能需要等待一整天)
  • 對參數敏感,調參是個技術活
  • 不直接輸出概率,需要額外處理
  • 對特征縮放很敏感

實用避坑技巧

  1. 線性核優先:文本分類就用線性核,別折騰那些花里胡哨的核函數
  2. 特征標準化:對TF-IDF等特征做L2歸一化,效果立竿見影
  3. 參數優化:主要調C參數,用網格搜索或隨機搜索
  4. 類別不平衡:用class_weight參數調整類別權重
  5. 概率輸出:用CalibratedClassifierCV包裝LinearSVC,這樣就能輸出概率了

決策樹與隨機森林分類器

決策樹(Decision Tree)和隨機森林(Random Forest)是另一類常用分類器,尤其是隨機森林,在文本分類中也表現不俗。

決策樹原理

決策樹通過一系列問題將數據劃分成不同的子集,直到葉節點足夠"純"為止。主要步驟包括:

  1. 特征選擇:選最佳特征作為分裂點

    • 信息增益(Information Gain)
    • 基尼不純度(Gini Impurity)
    • 方差減少(Variance Reduction)
  2. 樹的生長:遞歸地建子樹

  3. 剪枝:防止過擬合

隨機森林原理

隨機森林是多棵決策樹的集成,通過以下方式提高性能:

  1. Bagging(Bootstrap Aggregating):每棵樹用有放回抽樣的數據子集訓練
  2. 特征隨機選擇:每個節點只考慮特征的隨機子集
  3. 多數投票:集成多棵樹的預測結果

隨機森林實現

from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import Pipeline# 創建隨機森林分類器流水線
rf_pipeline = Pipeline([('vectorizer', TfidfVectorizer(max_features=5000)),('classifier', RandomForestClassifier(n_estimators=100,  # 樹的數量max_depth=None,    # 樹的最大深度min_samples_split=2,random_state=42))
])# 訓練模型
rf_pipeline.fit(X_train, y_train)# 預測
y_pred = rf_pipeline.predict(X_test)# 特征重要性
feature_names = rf_pipeline.named_steps['vectorizer'].get_feature_names_out()
importances = rf_pipeline.named_steps['classifier'].feature_importances_
indices = np.argsort(importances)[::-1]# 顯示前10個重要特征
print("特征重要性排名:")
for i in range(10):print(f"{i+1}. {feature_names[indices[i]]} ({importances[indices[i]]:.4f})")

優缺點分析

優點:

  • 抗過擬合能力強,比較穩健
  • 不用做特征篩選,它自己就能處理高維數據
  • 可以輸出特征重要性,幫你理解數據
  • 可以并行訓練,速度不錯
  • 對缺失值不敏感,省去了數據清洗的麻煩

缺點:

  • 在高維稀疏的文本數據上通常不如SVM和樸素貝葉斯
  • 內存消耗大,訓練大模型時電腦風扇狂轉
  • 噪聲大的數據上容易過擬合
  • 解釋性不如線性模型那么直觀

實用避坑技巧

  1. 特征選擇:用特征重要性找出關鍵詞,提升模型可解釋性
  2. 參數調優:重點調n_estimators(樹的數量)和max_depth(樹深度)
  3. 類別平衡:用class_weight參數處理不平衡數據
  4. 特征表示:隨機森林配TF-IDF特征效果比較好
  5. 集成方法:考慮和其他模型如GBM或AdaBoost組合使用

集成學習方法與優化策略

集成學習(Ensemble Learning)就是"三個臭皮匠頂個諸葛亮"的道理,結合多個基礎模型來提高性能。

主要集成方法

  1. 投票集成(Voting)

    • 硬投票(Hard Voting):少數服從多數
    • 軟投票(Soft Voting):加權平均概率
  2. Bagging:并行訓練,降低方差

    • Random Forest就是代表
    • Extra Trees更隨機一點
  3. Boosting:序列訓練,降低偏差

    • AdaBoost:關注錯誤樣本
    • Gradient Boosting:逐步糾正誤差
    • XGBoost:工業級GBDT實現
    • LightGBM:更快更輕量的GBDT

實現各種集成方法

from sklearn.ensemble import VotingClassifier, GradientBoostingClassifier, AdaBoostClassifier
from sklearn.linear_model import LogisticRegression# 準備基礎分類器
clf1 = Pipeline([('vect', CountVectorizer()), ('nb', MultinomialNB())])
clf2 = Pipeline([('vect', TfidfVectorizer()), ('svm', LinearSVC())])
clf3 = Pipeline([('vect', TfidfVectorizer()), ('lr', LogisticRegression())])# 投票集成
voting_clf = VotingClassifier(estimators=[('nb', clf1), ('svm', clf2), ('lr', clf3)],voting='hard'
)# 梯度提升
gb_clf = Pipeline([('vect', TfidfVectorizer()),('gb', GradientBoostingClassifier(n_estimators=100, learning_rate=0.1))
])# 選擇最佳集成模型
voting_clf.fit(X_train, y_train)
voting_accuracy = accuracy_score(y_test, voting_clf.predict(X_test))gb_clf.fit(X_train, y_train)
gb_accuracy = accuracy_score(y_test, gb_clf.predict(X_test))print(f"投票集成準確率: {voting_accuracy:.4f}")
print(f"梯度提升準確率: {gb_accuracy:.4f}")

Stacking集成高級實現

Stacking是更高級的集成方法,用一個元學習器(meta-learner)組合基礎模型的預測:

from sklearn.ensemble import StackingClassifier# 定義基礎分類器
base_classifiers = [('nb', clf1),('svm', clf2),('rf', Pipeline([('vect', TfidfVectorizer()), ('rf', RandomForestClassifier())]))
]# 定義元學習器
meta_classifier = LogisticRegression()# 創建Stacking模型
stacking_clf = StackingClassifier(estimators=base_classifiers,final_estimator=meta_classifier,cv=5  # 交叉驗證折數
)# 訓練和評估
stacking_clf.fit(X_train, y_train)
stacking_accuracy = accuracy_score(y_test, stacking_clf.predict(X_test))
print(f"Stacking集成準確率: {stacking_accuracy:.4f}")

在這里插入圖片描述

優化策略

  1. 模型選擇策略

    • 挑選不同類型的分類器組合(比如NB+SVM+RF),這樣多樣性更強
    • 或者用同一算法不同參數的模型
  2. 特征多樣化

    • 用不同的特征表示(詞袋、TF-IDF、詞嵌入)
    • 用不同的n-gram范圍
    • 用不同的預處理方法
  3. 集成權重優化

    • 根據驗證集性能調整權重
    • 用元學習器自動學習權重

實際項目經驗分享

在輿情分類項目中,單個最好的模型準確率只有87%,后來組合了NB+SVM+GBDT三個模型,準確率直接提到了92%。關鍵在于確保基礎模型有足夠的差異性,太相似的模型集成起來效果提升不明顯。

傳統機器學習方法可能看起來有點"老土",但在很多實際項目中,它們依然是首選方案,尤其是在計算資源有限、數據量不大的情況下。不過,隨著深度學習的發展,神經網絡模型在文本分類上確實提供了更好的性能上限,接下來我們就來看看這些"新銳"力量。

深度學習分類模型

隨著深度學習的興起,神經網絡模型在文本分類上展現出了驚人的性能。相比傳統方法,深度學習最大的優勢在于能自動學習特征表示,省去了大量人工特征工程的工作,同時能捕捉到更復雜的語義關系。

循環神經網絡(RNN)及其變體

循環神經網絡專門用來處理序列數據,文本本質上就是詞語的序列,所以RNN天生適合處理文本分類任務。

基本RNN原理

RNN最厲害的地方在于有"記憶"能力,它通過網絡中的循環連接,讓當前時刻的輸出不只取決于當前輸入,還取決于之前的狀態:

h t = σ ( W x ? x t + W h ? h t ? 1 + b h ) h_t = \sigma(W_x \cdot x_t + W_h \cdot h_{t-1} + b_h) ht?=σ(Wx??xt?+Wh??ht?1?+bh?)
y t = σ ( W y ? h t + b y ) y_t = \sigma(W_y \cdot h_t + b_y) yt?=σ(Wy??ht?+by?)

其中:

  • h t h_t ht?是t時刻的隱藏狀態(可以理解為"記憶")
  • x t x_t xt?是t時刻的輸入(比如一個詞的向量表示)
  • y t y_t yt?是t時刻的輸出
  • W x , W h , W y W_x, W_h, W_y Wx?,Wh?,Wy?是權重矩陣,需要學習
  • b h , b y b_h, b_y bh?,by?是偏置項
  • σ \sigma σ是激活函數

長短期記憶網絡(LSTM)

基本的RNN有個致命問題:梯度消失或爆炸,導致難以學習長距離依賴關系。比如一個句子開頭和結尾有聯系,基本RNN就學不會。于是就有了LSTM(Long Short-Term Memory)。

LSTM引入了三個門控機制:

  • 遺忘門(forget gate):決定扔掉哪些無用信息
  • 輸入門(input gate):決定更新哪些有用信息
  • 輸出門(output gate):決定輸出哪些當前狀態信息

LSTM的關鍵公式:

f t = σ ( W f ? [ h t ? 1 , x t ] + b f ) f_t = \sigma(W_f \cdot [h_{t-1}, x_t] + b_f) ft?=σ(Wf??[ht?1?,xt?]+bf?)
i t = σ ( W i ? [ h t ? 1 , x t ] + b i ) i_t = \sigma(W_i \cdot [h_{t-1}, x_t] + b_i) it?=σ(Wi??[ht?1?,xt?]+bi?)
C ~ t = tanh ? ( W C ? [ h t ? 1 , x t ] + b C ) \tilde{C}_t = \tanh(W_C \cdot [h_{t-1}, x_t] + b_C) C~t?=tanh(WC??[ht?1?,xt?]+bC?)
C t = f t ? C t ? 1 + i t ? C ~ t C_t = f_t * C_{t-1} + i_t * \tilde{C}_t Ct?=ft??Ct?1?+it??C~t?
o t = σ ( W o ? [ h t ? 1 , x t ] + b o ) o_t = \sigma(W_o \cdot [h_{t-1}, x_t] + b_o) ot?=σ(Wo??[ht?1?,xt?]+bo?)
h t = o t ? tanh ? ( C t ) h_t = o_t * \tanh(C_t) ht?=ot??tanh(Ct?)

這公式看著挺復雜,實際上就是通過幾個門控單元來控制信息的流動。

門控循環單元(GRU)

GRU(Gated Recurrent Unit)是LSTM的簡化版,效果差不多但計算更高效:

  • 只有兩個門:更新門和重置門
  • 沒有單獨的記憶單元
  • 參數更少,訓練更快

使用Keras實現RNN文本分類

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout, Bidirectional
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences# 數據預處理
max_words = 10000  # 詞匯表大小
max_len = 200      # 序列最大長度# 創建詞匯表
tokenizer = Tokenizer(num_words=max_words)
tokenizer.fit_on_texts(X_train)# 將文本轉換為序列
X_train_seq = tokenizer.texts_to_sequences(X_train)
X_test_seq = tokenizer.texts_to_sequences(X_test)# 填充序列
X_train_pad = pad_sequences(X_train_seq, maxlen=max_len)
X_test_pad = pad_sequences(X_test_seq, maxlen=max_len)# 構建雙向LSTM模型
model = Sequential()
model.add(Embedding(max_words, 128, input_length=max_len))
model.add(Bidirectional(LSTM(64, return_sequences=True)))
model.add(Bidirectional(LSTM(32)))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(len(np.unique(y_train)), activation='softmax'))# 編譯模型
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy']
)# 模型摘要
model.summary()# 訓練模型
history = model.fit(X_train_pad, y_train,epochs=10,batch_size=32,validation_split=0.2,verbose=1
)# 評估模型
loss, accuracy = model.evaluate(X_test_pad, y_test)
print(f"測試準確率: {accuracy:.4f}")

實用優化技巧

  1. 雙向RNN(Bidirectional RNN)

    • 同時從前往后和從后往前處理文本
    • 能捕捉更全面的上下文信息
    • 實現起來超簡單,就是加個Bidirectional包裝層
  2. 多層RNN

    • 堆疊多個RNN層,提高模型表達能力
    • 一般2-3層就夠了,不用堆太多
    • 記得中間層設置return_sequences=True,否則后面的層沒法用
  3. 注意力機制(Attention)

    • 讓模型能關注序列中更重要的部分
    • 常跟LSTM一起用,效果很明顯
    • 實現示例:
    from tensorflow.keras.layers import Attention, Dense# 簡化版自注意力實現
    attention_layer = Attention()([lstm_output, lstm_output])
    
  4. 梯度裁剪(Gradient Clipping)

    • 解決梯度爆炸問題
    • 實現:optimizer=tf.keras.optimizers.Adam(clipvalue=1.0)

使用場景與性能分析

  • LSTM/GRU特別適合處理長文本,能捕捉長距離依賴
  • 在情感分析等需要理解整體語義的任務上表現優秀
  • 計算成本中等,訓練時間比傳統機器學習方法長
  • 在中小規模數據集上容易過擬合,需要正則化

卷積神經網絡(CNN)文本分類

卷積神經網絡(Convolutional Neural Network, CNN)最初設計用于圖像處理,但后來被發現在文本分類中也有出色表現。

CNN文本分類原理

在文本分類中,CNN主要通過以下方式工作:

  1. 詞嵌入層:將文本轉換為詞向量序列
  2. 卷積層:使用不同大小的過濾器捕獲n-gram特征
  3. 池化層:通常使用最大池化提取最顯著特征
  4. 全連接層:進行最終分類

在這里插入圖片描述

實現CNN文本分類

from tensorflow.keras.layers import Conv1D, GlobalMaxPooling1D# 構建CNN模型
model = Sequential()
model.add(Embedding(max_words, 128, input_length=max_len))
model.add(Conv1D(128, 5, activation='relu'))
model.add(GlobalMaxPooling1D())
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(len(np.unique(y_train)), activation='softmax'))# 編譯模型
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy', metrics=['accuracy']
)# 訓練模型
history = model.fit(X_train_pad, y_train,epochs=10,batch_size=64,validation_split=0.2
)

多尺度CNN(Multi-scale CNN)

使用不同大小的過濾器捕獲不同粒度的特征:

from tensorflow.keras.layers import Concatenate, Input
from tensorflow.keras.models import Model# 定義輸入
input_layer = Input(shape=(max_len,))
embedding_layer = Embedding(max_words, 128)(input_layer)# 不同大小的卷積核
conv1 = Conv1D(128, 3, activation='relu')(embedding_layer)
pool1 = GlobalMaxPooling1D()(conv1)conv2 = Conv1D(128, 4, activation='relu')(embedding_layer)
pool2 = GlobalMaxPooling1D()(conv2)conv3 = Conv1D(128, 5, activation='relu')(embedding_layer)
pool3 = GlobalMaxPooling1D()(conv3)# 合并不同卷積結果
concat = Concatenate()([pool1, pool2, pool3])# 全連接層
dense = Dense(64, activation='relu')(concat)
dropout = Dropout(0.5)(dense)
output = Dense(len(np.unique(y_train)), activation='softmax')(dropout)# 構建模型
model = Model(inputs=input_layer, outputs=output)

性能比較與適用場景

CNN vs RNN:

  • CNN訓練速度更快,并行度高
  • CNN捕獲局部特征優秀,但難以捕獲長距離依賴
  • CNN在短文本分類(如推文、標題)表現突出
  • CNN模型更小,部署更方便

實際項目中,我常在關鍵詞提取和短文本分類任務中使用CNN。例如,在一個產品評論分類項目中,CNN只用了LSTM一半的訓練時間就達到了相近的準確率。

注意力機制與Transformer架構

注意力機制(Attention Mechanism)和基于它的Transformer架構是近年來NLP領域最重要的突破之一,它們極大地提高了文本分類的性能。

注意力機制原理

注意力機制讓模型能夠"關注"輸入序列中的特定部分,計算每個位置的重要性權重:

Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V Attention(Q,K,V)=softmax(dk? ?QKT?)V

其中:

  • Q Q Q:查詢矩陣(Query)
  • K K K:鍵矩陣(Key)
  • V V V:值矩陣(Value)
  • d k d_k dk?:鍵向量的維度

Transformer架構

Transformer完全基于注意力機制,摒棄了RNN和CNN:

  1. 多頭自注意力(Multi-head Self-attention):允許模型同時關注不同位置
  2. 位置編碼(Positional Encoding):彌補丟失的位置信息
  3. 前饋神經網絡(Feed-forward Network):對每個位置獨立應用
  4. 殘差連接和層歸一化:穩定訓練

在這里插入圖片描述

使用Transformer進行文本分類

import tensorflow as tf
from tensorflow.keras.layers import Input, Dense, Dropout, LayerNormalization
from tensorflow.keras.layers import MultiHeadAttention
from tensorflow.keras.models import Modeldef transformer_encoder(inputs, head_size, num_heads, ff_dim, dropout=0):# 多頭自注意力attention_output = MultiHeadAttention(key_dim=head_size, num_heads=num_heads, dropout=dropout)(inputs, inputs)attention_output = Dropout(dropout)(attention_output)attention_output = LayerNormalization(epsilon=1e-6)(inputs + attention_output)# 前饋網絡ffn_output = Dense(ff_dim, activation="relu")(attention_output)ffn_output = Dense(inputs.shape[-1])(ffn_output)ffn_output = Dropout(dropout)(ffn_output)return LayerNormalization(epsilon=1e-6)(attention_output + ffn_output)# 構建模型
def build_transformer_model(max_words=10000,max_len=200,embed_dim=128,num_heads=2,ff_dim=128,num_classes=5
):inputs = Input(shape=(max_len,))embedding_layer = Embedding(max_words, embed_dim)(inputs)# 添加位置編碼positions = tf.range(start=0, limit=max_len, delta=1)position_embedding = Embedding(max_len, embed_dim)(positions)x = embedding_layer + position_embedding# Transformer塊transformer_block = transformer_encoder(x, embed_dim//num_heads, num_heads, ff_dim)# 全局池化x = tf.reduce_mean(transformer_block, axis=1)# 輸出層x = Dense(ff_dim, activation="relu")(x)x = Dropout(0.1)(x)outputs = Dense(num_classes, activation="softmax")(x)model = Model(inputs=inputs, outputs=outputs)return model# 實例化模型
transformer_model = build_transformer_model(num_classes=len(np.unique(y_train))
)# 編譯和訓練
transformer_model.compile(optimizer="adam",loss="sparse_categorical_crossentropy",metrics=["accuracy"]
)history = transformer_model.fit(X_train_pad, y_train,batch_size=32,epochs=5,validation_split=0.2
)

優化技巧

  1. 預熱學習率(Learning Rate Warmup)

    • 學習率先增加后降低,穩定訓練
    • 實現方法:自定義學習率調度器
  2. 梯度累積(Gradient Accumulation)

    • 在更新前累積多個批次的梯度
    • 允許使用更大的有效批次大小
  3. 層丟棄(Layer Dropout)

    • 訓練時隨機跳過某些層
    • 減少過擬合,提高泛化能力

預訓練語言模型的微調應用

預訓練語言模型(Pre-trained Language Models, PLM)如BERT、RoBERTa等代表了NLP的最新進展,它們通過在大規模語料上預訓練,然后在特定任務上微調,實現了優異的性能。

預訓練語言模型的工作原理

預訓練+微調范式:

  1. 預訓練階段:在大規模無標注語料上進行自監督學習
    • 掩碼語言模型(MLM)
    • 下一句預測(NSP)
  2. 微調階段:在特定任務數據上調整模型參數

BERT微調架構

對于文本分類任務,BERT的微調相對簡單:

  1. 輸入文本加上特殊標記:[CLS] text [SEP]
  2. [CLS]標記對應的輸出作為整個序列的表示
  3. 在此表示上加一個分類層進行預測

在這里插入圖片描述

使用Transformers庫實現BERT微調

import torch
from torch.utils.data import TensorDataset, DataLoader, RandomSampler
from transformers import BertTokenizer, BertForSequenceClassification, AdamW
from sklearn.preprocessing import LabelEncoder# 初始化tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')# 數據處理函數
def prepare_data(texts, labels, max_length=128):# 編碼文本encodings = tokenizer(texts.tolist(),truncation=True,padding='max_length',max_length=max_length,return_tensors='pt')# 轉換標簽label_encoder = LabelEncoder()encoded_labels = label_encoder.fit_transform(labels)labels_tensor = torch.tensor(encoded_labels)# 創建數據集dataset = TensorDataset(encodings['input_ids'],encodings['attention_mask'],labels_tensor)return dataset, label_encoder# 準備訓練和測試數據
train_dataset, label_encoder = prepare_data(X_train, y_train)
test_dataset, _ = prepare_data(X_test, y_test, label_encoder)# 創建數據加載器
batch_size = 16
train_dataloader = DataLoader(train_dataset,sampler=RandomSampler(train_dataset),batch_size=batch_size
)# 初始化模型
model = BertForSequenceClassification.from_pretrained('bert-base-uncased',num_labels=len(label_encoder.classes_)
)# 設置優化器
optimizer = AdamW(model.parameters(), lr=2e-5)# 訓練函數
def train_model(model, dataloader, optimizer, epochs=3):device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')model.to(device)for epoch in range(epochs):model.train()total_loss = 0for batch in dataloader:batch = tuple(t.to(device) for t in batch)input_ids, attention_mask, labels = batch# 清除之前的梯度optimizer.zero_grad()# 前向傳播outputs = model(input_ids=input_ids,attention_mask=attention_mask,labels=labels)loss = outputs.losstotal_loss += loss.item()# 反向傳播loss.backward()# 更新參數optimizer.step()avg_loss = total_loss / len(dataloader)print(f"Epoch {epoch+1} - Average loss: {avg_loss:.4f}")return model# 訓練模型
trained_model = train_model(model, train_dataloader, optimizer)# 評估函數
def evaluate_model(model, dataset):device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')model.to(device)model.eval()dataloader = DataLoader(dataset, batch_size=32)all_preds = []all_labels = []with torch.no_grad():for batch in dataloader:batch = tuple(t.to(device) for t in batch)input_ids, attention_mask, labels = batchoutputs = model(input_ids=input_ids,attention_mask=attention_mask)logits = outputs.logitspreds = torch.argmax(logits, dim=1).cpu().numpy()all_preds.extend(preds)all_labels.extend(labels.cpu().numpy())accuracy = (np.array(all_preds) == np.array(all_labels)).mean()return accuracy# 評估模型
accuracy = evaluate_model(trained_model, test_dataset)
print(f"BERT模型測試準確率: {accuracy:.4f}")

優化BERT微調性能的技巧

  1. 學習率預熱和線性衰減

    • 推薦學習率:2e-5到5e-5
    • 訓練中逐漸降低學習率
  2. 梯度累積

    • 解決顯存不足問題
    • 模擬更大的批次大小
  3. 混合精度訓練

    • 使用FP16減少顯存使用
    • 加速訓練過程
  4. 模型剪枝和蒸餾

    • 減小模型體積
    • 加速推理速度
  5. 使用更小的模型變體

    • BERT-small或DistilBERT
    • 性能略有下降但速度大幅提升

各種預訓練模型比較

模型參數量特點適用場景
BERT110M/340M雙向編碼器通用NLP任務
RoBERTa125M/355M優化訓練方法的BERT需要高準確率
DistilBERT67M輕量級BERT資源受限環境
ALBERT12M/18M參數共享內存受限設備
XLNet110M/340M自回歸預訓練長文本理解

在實際項目中,如果計算資源充足,預訓練模型通常能提供最佳性能。例如,在一個法律文檔分類任務中,BERT模型的準確率比傳統機器學習方法高出了近8個百分點。但這些模型的計算開銷也很大,如果追求速度和資源效率,傳統方法仍有其價值。

深度學習模型在文本分類領域展現出了卓越的性能,但要發揮這些模型的潛力,合適的文本特征工程仍然至關重要。接下來,我們將深入探討文本特征工程的各種方法和技巧。

文本特征工程

雖說深度學習模型能自動學習特征,但好的文本特征工程依然能大幅提升分類效果。尤其是對傳統機器學習模型,特征工程簡直就是成敗的關鍵。

詞袋模型與TF-IDF表示

詞袋模型(Bag of Words, BoW)和TF-IDF可能是最基礎也是用得最多的文本特征表示方法了。

詞袋模型(BoW)

詞袋模型就是把文本表示成詞頻向量,完全不考慮詞的順序和語法:

  1. 先建一個詞匯表,包含語料庫里所有不重復的詞
  2. 對每個文檔,統計詞匯表中每個詞出現了幾次
  3. 生成一個固定長度的特征向量
from sklearn.feature_extraction.text import CountVectorizer# 創建詞袋模型
count_vectorizer = CountVectorizer(max_features=5000,  # 限制詞匯表大小,太大了內存扛不住min_df=5,          # 詞至少在5個文檔中出現才保留max_df=0.7,        # 出現在超過70%文檔的詞被認為是停用詞stop_words='english'  # 過濾英文停用詞,中文得自定義
)# 擬合并轉換訓練數據
X_train_bow = count_vectorizer.fit_transform(X_train)# 只轉換測試數據
X_test_bow = count_vectorizer.transform(X_test)print(f"特征維度: {X_train_bow.shape}")
print(f"詞匯表大小: {len(count_vectorizer.vocabulary_)}")# 看看前10個詞
print("詞匯表示例:")
for word, idx in sorted(count_vectorizer.vocabulary_.items(), key=lambda x: x[1])[:10]:print(f"{idx}: {word}")

TF-IDF(Term Frequency-Inverse Document Frequency)

TF-IDF通過加權詞頻解決了詞袋模型中常見詞權重過高的問題:

  1. TF(Term Frequency):詞在文檔中出現的頻率
    T F ( t , d ) = n t , d ∑ s ∈ d n s , d TF(t,d) = \frac{n_{t,d}}{\sum_{s \in d} n_{s,d}} TF(t,d)=sd?ns,d?nt,d??

  2. IDF(Inverse Document Frequency):衡量詞的稀有程度
    I D F ( t , D ) = log ? ∣ D ∣ ∣ { d ∈ D : t ∈ d } ∣ IDF(t,D) = \log \frac{|D|}{|\{d \in D: t \in d\}|} IDF(t,D)=log{dD:td}D?

  3. TF-IDF:兩者相乘
    T F I D F ( t , d , D ) = T F ( t , d ) × I D F ( t , D ) TFIDF(t,d,D) = TF(t,d) \times IDF(t,D) TFIDF(t,d,D)=TF(t,d)×IDF(t,D)

簡單說就是:常見詞被降權,稀有詞被升權。比如"的"、"是"這種高頻詞的權重會很低,而"神經網絡"這種專業詞的權重會高一些。

from sklearn.feature_extraction.text import TfidfVectorizer# 創建TF-IDF向量化器
tfidf_vectorizer = TfidfVectorizer(max_features=5000,min_df=5,max_df=0.7,stop_words='english',norm='l2',        # L2歸一化,避免長文本值偏大use_idf=True,     # 當然要用IDF啦smooth_idf=True,  # 平滑IDF防止分母為零sublinear_tf=True # 用1+log(tf)代替tf,進一步壓制高頻詞
)# 擬合并轉換
X_train_tfidf = tfidf_vectorizer.fit_transform(X_train)
X_test_tfidf = tfidf_vectorizer.transform(X_test)print(f"TF-IDF特征維度: {X_train_tfidf.shape}")# 看看特征值分布
tfidf_array = X_train_tfidf.toarray()
print(f"TF-IDF平均值: {tfidf_array.mean():.6f}")
print(f"TF-IDF最大值: {tfidf_array.max():.6f}")
print(f"TF-IDF最小值: {tfidf_array.min():.6f}")

BoW vs TF-IDF

兩種方法的對比:

特性詞袋模型TF-IDF
特征解釋性高(就是詞頻)中(加權詞頻)
對常見詞的處理權重高權重低
對罕見詞的處理權重低權重高
計算復雜度
適用場景主題分類關鍵詞提取,搜索

實用優化技巧

  1. n-gram特征:捕捉短語和上下文

    # 用1-gram和2-gram
    tfidf_ngram = TfidfVectorizer(ngram_range=(1,2), max_features=10000)
    

    這樣"機器學習"就會被當作一個整體特征,而不是分開的"機器"和"學習"。

  2. 特征選擇:去掉無關特征

    from sklearn.feature_selection import chi2, SelectKBest# 用卡方檢驗選擇特征
    selector = SelectKBest(chi2, k=1000)
    X_train_selected = selector.fit_transform(X_train_tfidf, y_train)
    X_test_selected = selector.transform(X_test_tfidf)
    
  3. 特征歸一化:提升模型效果

    from sklearn.preprocessing import normalize# L2歸一化
    X_train_normalized = normalize(X_train_tfidf, norm='l2')
    
  4. 自定義預處理:提高特征質量

    # 自定義標記化和停用詞處理
    def custom_preprocessor(text):# 轉小寫text = text.lower()# 去掉特殊字符text = re.sub(r'[^\w\s]', '', text)# 去掉數字text = re.sub(r'\d+', '', text)return textvectorizer = TfidfVectorizer(preprocessor=custom_preprocessor)
    

在這里插入圖片描述

詞嵌入特征與文檔向量

詞嵌入(Word Embeddings)把詞映射到低維連續向量空間,能捕獲語義關系,是現代NLP的基礎技術。

主要詞嵌入技術

  1. Word2Vec

    • 兩種模型:CBOW(用上下文預測目標詞)和Skip-gram(用目標詞預測上下文)
    • 通過一個淺層神經網絡訓練得到
    • 能學到一些神奇的語義關系,比如"王-男+女=王后"這樣的詞向量運算
  2. GloVe(Global Vectors)

    • 結合全局矩陣分解和局部上下文窗口
    • 能捕獲全局共現統計信息
  3. FastText

    • Word2Vec的升級版,考慮子詞單元
    • 能處理OOV(訓練集中沒見過的詞)
    • 特別適合形態豐富的語言(如德語)和有很多復合詞的場景

使用預訓練詞嵌入

import gensim.downloader as api
from gensim.models import KeyedVectors
import numpy as np# 加載預訓練詞向量
word_vectors = api.load("glove-wiki-gigaword-100")  # 100維GloVe# 創建文檔向量(簡單取平均)
def document_vector(doc, model, dim=100):# 分詞words = doc.lower().split()# 過濾不在詞表中的詞words = [word for word in words if word in model.key_to_index]if len(words) == 0:return np.zeros(dim)# 求所有詞向量的平均return np.mean([model[word] for word in words], axis=0)# 把所有文檔轉成向量
X_train_wv = np.array([document_vector(doc, word_vectors) for doc in X_train])
X_test_wv = np.array([document_vector(doc, word_vectors) for doc in X_test])print(f"詞嵌入特征維度: {X_train_wv.shape}")

訓練自定義詞嵌入

有時候預訓練的詞向量并不適合你的特定領域(比如醫療、法律文本),這時就需要訓練自己的詞嵌入:

from gensim.models import Word2Vec
from gensim.utils import simple_preprocess# 準備訓練數據
def preprocess_text(text):return simple_preprocess(text, deacc=True)  # deacc=True移除重音符號# 分詞
tokenized_train = [preprocess_text(doc) for doc in X_train]# 訓練Word2Vec模型
w2v_model = Word2Vec(sentences=tokenized_train,vector_size=100,   # 詞向量維度window=5,          # 上下文窗口大小min_count=5,       # 詞頻閾值workers=4,         # 并行數sg=1               # 用Skip-gram模型,對小數據集效果更好
)# 保存模型
w2v_model.save("word2vec_model.bin")# 用訓練好的模型生成文檔向量
X_train_custom_wv = np.array([document_vector(doc, w2v_model.wv) for doc in X_train])

Doc2Vec文檔嵌入

Doc2Vec直接學習文檔級別的嵌入表示,避免了詞向量簡單平均的問題:

from gensim.models.doc2vec import Doc2Vec, TaggedDocument# 準備訓練數據
tagged_docs = [TaggedDocument(words=preprocess_text(doc), tags=[i]) for i, doc in enumerate(X_train)]# 訓練Doc2Vec模型
d2v_model = Doc2Vec(documents=tagged_docs,vector_size=100,window=5,min_count=5,workers=4,epochs=20
)# 生成文檔向量
X_train_d2v = np.array([d2v_model.infer_vector(preprocess_text(doc)) for doc in X_train])
X_test_d2v = np.array([d2v_model.infer_vector(preprocess_text(doc)) for doc in X_test])

嵌入特征的優化技巧

  1. 詞向量微調

    • 在目標任務上微調預訓練詞向量
    • 記得用預訓練向量初始化嵌入層
  2. 詞向量加權平均

    • 用TF-IDF權重加權詞向量
    • 這比簡單平均效果好很多
    def tfidf_weighted_doc_vector(doc, tfidf_model, word_vectors, dim=100):# 分詞words = preprocess_text(doc)# 獲取TF-IDF權重tfidf_vector = tfidf_model.transform([' '.join(words)])[0]# 詞到索引的映射word_indices = {word: idx for idx, word in enumerate(tfidf_model.get_feature_names_out())}weighted_vector = np.zeros(dim)weight_sum = 0for word in words:if word in word_vectors.key_to_index and word in word_indices:# 獲取詞的TF-IDF權重idx = word_indices[word]if idx < len(tfidf_vector.indices) and tfidf_vector.indices[idx] < len(tfidf_vector.data):tfidf_weight = tfidf_vector[idx]# 加權詞向量weighted_vector += tfidf_weight * word_vectors[word]weight_sum += tfidf_weightif weight_sum > 0:weighted_vector /= weight_sumreturn weighted_vector
    
  3. 層次化嵌入

    • 構建句子級和文檔級的分層表示
    • 更好地捕獲結構信息
  4. 注意力加權

    • 用自注意力機制加權詞向量
    • 自動學習關鍵詞的重要性

詞嵌入vs傳統特征

特性詞袋/TF-IDF詞嵌入
維度高維稀疏(幾萬維)低維稠密(幾百維)
語義信息很少豐富
訓練難度簡單復雜
內存占用稀疏矩陣省內存密集矩陣小
OOV問題嚴重部分緩解
適用模型傳統ML深度學習

在這里插入圖片描述

在一個電影評論情感分析項目中,我從最簡單的TF-IDF特征開始,準確率是82%,換成詞嵌入特征后提高到了87%,再用BERT預訓練模型直接達到了92%。不同的特征表示方法確實能帶來很大差異。

N-gram特征與上下文信息

N-gram就是從文本中提取的連續N個詞或字符的序列,能捕獲局部上下文信息,彌補詞袋模型忽略詞序的缺點。

N-gram的類型

  1. 詞級N-gram:連續N個詞的序列

    • Unigram(1-gram):單個詞,如"python"
    • Bigram(2-gram):兩個詞,如"machine learning"
    • Trigram(3-gram):三個詞,如"support vector machine"
  2. 字符級N-gram:連續N個字符的序列

    • 比如:“text"的3-gram是"tex"和"ext”
    • 對拼寫錯誤和未知詞更健壯
    • 特別適合中文、日文等沒有明確詞邊界的語言

實現N-gram特征

# 詞級N-gram
word_ngram_vectorizer = CountVectorizer(ngram_range=(1, 3),  # 提取1-gram到3-grammax_features=10000
)X_train_word_ngram = word_ngram_vectorizer.fit_transform(X_train)
X_test_word_ngram = word_ngram_vectorizer.transform(X_test)print(f"詞級N-gram特征維度: {X_train_word_ngram.shape}")# 字符級N-gram
char_ngram_vectorizer = CountVectorizer(analyzer='char',     # 字符級分析ngram_range=(3, 6),  # 3到6個字符max_features=10000
)X_train_char_ngram = char_ngram_vectorizer.fit_transform(X_train)
X_test_char_ngram = char_ngram_vectorizer.transform(X_test)print(f"字符級N-gram特征維度: {X_train_char_ngram.shape}")

N-gram與TF-IDF結合

ngram_tfidf_vectorizer = TfidfVectorizer(ngram_range=(1, 2),max_features=10000,sublinear_tf=True
)X_train_ngram_tfidf = ngram_tfidf_vectorizer.fit_transform(X_train)
X_test_ngram_tfidf = ngram_tfidf_vectorizer.transform(X_test)# 看看部分特征
feature_names = ngram_tfidf_vectorizer.get_feature_names_out()
print("N-gram特征示例:")
for i in range(10):print(feature_names[i])

優化N-gram特征

  1. 最大特征數量

    • N-gram特征數量是指數級增長的
    • max_features參數能控制特征數量
    • 對于2-gram和3-gram,特征數量爆炸是常態
  2. 最小文檔頻率

    • 過濾掉罕見的N-gram
    • min_df參數設置閾值
    • 減少噪聲和計算量
  3. 混合不同級別的N-gram

    • 組合詞級和字符級N-gram,優勢互補
    • 融合多種長度的N-gram
  4. 特征選擇

    • 用互信息或卡方檢驗篩選最有區分度的N-gram
    • 降維的同時提高效果

N-gram的適用場景

  • 短文本分類:N-gram在短文本中特別有效,因為上下文有限
  • 情感分析:能捕獲關鍵短語("not good"和"good"是完全不同的)
  • 多語言文本:字符級N-gram跨語言效果不錯
  • 拼寫容錯:字符級N-gram對拼寫錯誤不敏感

我在做一個用戶評論分類項目時,單詞級特征的準確率只有75%,加上2-gram后提高到了83%,因為很多評論中的關鍵信息存在于詞組中,比如"太貴了"和"不太貴"意思完全相反,但詞袋模型無法區分。

特征選擇與降維技術

文本特征通常維度高且有冗余,用特征選擇和降維技術可以提升模型性能,還能加速訓練。

基于統計的特征選擇

  1. 卡方檢驗(Chi-square)

    • 測量特征與目標變量的獨立性
    • 值越大表明相關性越強
    from sklearn.feature_selection import SelectKBest, chi2# 選最相關的1000個特征
    chi2_selector = SelectKBest(chi2, k=1000)
    X_train_chi2 = chi2_selector.fit_transform(X_train_tfidf, y_train)
    X_test_chi2 = chi2_selector.transform(X_test_tfidf)# 查看選出的特征
    selected_features = chi2_selector.get_support(indices=True)
    selected_feature_names = [feature_names[i] for i in selected_features]
    
  2. 互信息(Mutual Information)

    • 衡量特征與標簽共享的信息量
    • 適用于分類和回歸
    from sklearn.feature_selection import mutual_info_classifmi_selector = SelectKBest(mutual_info_classif, k=1000)
    X_train_mi = mi_selector.fit_transform(X_train_tfidf, y_train)
    
  3. 方差閾值(Variance Threshold)

    • 移除低方差特征
    • 無監督選擇方法,不考慮標簽
    • 適合預處理階段
    from sklearn.feature_selection import VarianceThreshold# 移除方差低于閾值的特征
    var_selector = VarianceThreshold(threshold=0.1)
    X_train_var = var_selector.fit_transform(X_train_tfidf)
    

降維技術

  1. 主成分分析(PCA)

    • 線性降維方法
    • 保留數據最大方差方向
    from sklearn.decomposition import PCA# 降至100維
    pca = PCA(n_components=100)
    X_train_pca = pca.fit_transform(X_train_tfidf.toarray())
    X_test_pca = pca.transform(X_test_tfidf.toarray())# 查看方差解釋率
    explained_variance = pca.explained_variance_ratio_
    print(f"前10個成分的方差解釋率: {explained_variance[:10]}")
    print(f"總方差解釋率: {sum(explained_variance):.4f}")
    
  2. 截斷奇異值分解(Truncated SVD)

    • 專門為稀疏矩陣設計的降維方法
    • 不用轉成密集矩陣,內存友好
    from sklearn.decomposition import TruncatedSVDsvd = TruncatedSVD(n_components=100, random_state=42)
    X_train_svd = svd.fit_transform(X_train_tfidf)
    X_test_svd = svd.transform(X_test_tfidf)print(f"SVD方差解釋率: {sum(svd.explained_variance_ratio_):.4f}")
    
  3. 非負矩陣分解(NMF)

    • 分解為非負矩陣的乘積
    • 適合文本這類非負數據
    • 能提取主題
    from sklearn.decomposition import NMFnmf = NMF(n_components=100, random_state=42)
    X_train_nmf = nmf.fit_transform(X_train_tfidf)
    X_test_nmf = nmf.transform(X_test_tfidf)
    
  4. t-SNE與UMAP

    • 非線性降維,保留局部結構
    • 主要用于可視化
    from sklearn.manifold import TSNE
    import umap# t-SNE計算很慢,一般用于可視化
    tsne = TSNE(n_components=2, random_state=42)
    X_sample_tsne = tsne.fit_transform(X_train_tfidf[:1000].toarray())# UMAP是更快的替代方案
    reducer = umap.UMAP(random_state=42)
    X_sample_umap = reducer.fit_transform(X_train_tfidf[:1000].toarray())
    

在這里插入圖片描述

實用案例:優化分類性能

from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV
from sklearn.svm import LinearSVC# 構建特征工程流水線
feature_pipeline = Pipeline([('tfidf', TfidfVectorizer(max_features=10000, ngram_range=(1, 2))),('chi2', SelectKBest(chi2)),('svd', TruncatedSVD())
])# 完整分類流水線
pipeline = Pipeline([('features', feature_pipeline),('classifier', LinearSVC())
])# 參數網格
param_grid = {'features__tfidf__ngram_range': [(1, 1), (1, 2)],'features__chi2__k': [1000, 5000],'features__svd__n_components': [100, 200],'classifier__C': [0.1, 1.0, 10.0]
}# 網格搜索
grid_search = GridSearchCV(pipeline, param_grid,cv=5, scoring='accuracy', n_jobs=-1
)grid_search.fit(X_train, y_train)# 最佳參數和性能
print(f"最佳參數: {grid_search.best_params_}")
print(f"最佳交叉驗證得分: {grid_search.best_score_:.4f}")# 測試集評估
best_pipeline = grid_search.best_estimator_
test_accuracy = best_pipeline.score(X_test, y_test)
print(f"測試集準確率: {test_accuracy:.4f}")

特征工程的經驗法則

  1. 維度與樣本量平衡

    • 特征數量應與樣本數量相匹配
    • 太多特征容易過擬合
    • 經驗上,特征數最好不超過樣本數的10%
  2. 計算效率與性能平衡

    • 特征工程越復雜,回報往往遞減
    • 先從簡單特征開始,逐步增加復雜度
    • 有時候簡單的TF-IDF就夠了
  3. 領域知識很重要

    • 針對特定領域構建專業詞表
    • 定制停用詞和領域詞典
    • 利用行業術語和標準
  4. 組合特征的威力

    • 混合不同類型的特征往往效果最好
    • 詞袋+N-gram+詞嵌入的組合經常能獲得最佳效果
    • 打造"特征工程全家桶"

好的文本特征工程仍然是文本分類成功的關鍵,即使在深度學習時代也不例外。接下來,我們要看看一些高級優化策略,解決實際應用中常遇到的挑戰。

高級優化與實踐策略

搞定了基礎模型和特征工程后,還有一些高級優化策略能幫我們應對實際項目中的各種挑戰。這些策略往往能讓你的模型在性能上更上一層樓。

樣本不平衡問題解決方案

實際項目中,不同類別的樣本數量往往相差懸殊。比如無用的郵件分類,正常郵件可能占95%,無用的郵件只有5%。這會導致模型偏向多數類,少數類分類效果差。

常見解決方法

  1. 數據層面

    • 上采樣(Oversampling):增加少數類樣本

      from imblearn.over_sampling import RandomOverSampler, SMOTE# 隨機上采樣,簡單粗暴
      ros = RandomOverSampler(random_state=42)
      X_resampled, y_resampled = ros.fit_resample(X, y)# SMOTE(合成少數類過采樣),生成合成樣本而不是簡單復制
      smote = SMOTE(random_state=42)
      X_smote, y_smote = smote.fit_resample(X, y)
      
    • 下采樣(Undersampling):減少多數類樣本

      from imblearn.under_sampling import RandomUnderSamplerrus = RandomUnderSampler(random_state=42)
      X_resampled, y_resampled = rus.fit_resample(X, y)
      
    • 混合采樣:結合上采樣和下采樣

      from imblearn.combine import SMOTETomeksmt = SMOTETomek(random_state=42)
      X_resampled, y_resampled = smt.fit_resample(X, y)
      
  2. 算法層面

    • 類別權重:對少數類樣本賦予更高權重

      # 在SVM中使用類別權重
      svm = LinearSVC(class_weight='balanced')# 在深度學習中使用類別權重
      class_weights = {i: len(y) / (len(np.unique(y)) * np.sum(y == i)) for i in np.unique(y)}
      model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'],loss_weights=class_weights)
      
    • 調整決策閾值:根據驗證集調整分類閾值

      # 對概率輸出調整閾值
      from sklearn.metrics import precision_recall_curvey_scores = model.predict_proba(X_val)[:, 1]
      precisions, recalls, thresholds = precision_recall_curve(y_val, y_scores)# 找到最佳閾值(例如F1最大)
      f1_scores = 2 * (precisions * recalls) / (precisions + recalls)
      best_threshold = thresholds[np.argmax(f1_scores)]# 使用最佳閾值預測
      y_pred = (model.predict_proba(X_test)[:, 1] >= best_threshold).astype(int)
      
  3. 評估層面

    • 使用合適的評估指標:如F1分數、PR曲線
    • 分層抽樣(Stratified Sampling):保持訓練集和測試集的類別分布一致

實際項目案例分析

在一個醫療文本分類項目中,疾病類別嚴重不平衡(罕見病例只占1%)。我采用了以下策略:

  1. 對訓練數據使用SMOTE過采樣,使各類別樣本數相近
  2. 在模型訓練中設置class_weight=‘balanced’
  3. 使用F1分數代替準確率作為評估指標
  4. 對預測概率調整閾值,優化少數類的召回率

結果:罕見疾病的識別率從最初的不到20%提升到了78%。

多標簽分類與層次分類技術

除了標準的單標簽分類,文本分類還有兩種重要變種:多標簽分類和層次分類。

多標簽分類(Multi-label Classification)

多標簽分類允許一個文檔同時屬于多個類別,例如一篇新聞可能同時屬于"政治"和"經濟"。

  1. 問題轉換方法

    • 二元關聯法(Binary Relevance):為每個標簽訓練一個二分類器

      from sklearn.multioutput import MultiOutputClassifier
      from sklearn.linear_model import LogisticRegression# 多標簽分類器
      clf = MultiOutputClassifier(LogisticRegression())
      clf.fit(X_train, y_train_multilabel)
      
    • 分類器鏈(Classifier Chains):考慮標簽間的相關性

      from sklearn.multioutput import ClassifierChain# 分類器鏈
      chain = ClassifierChain(LogisticRegression())
      chain.fit(X_train, y_train_multilabel)
      
  2. 神經網絡實現

    # 多標簽CNN模型
    model = Sequential()
    model.add(Embedding(max_words, 128, input_length=max_len))
    model.add(Conv1D(128, 5, activation='relu'))
    model.add(GlobalMaxPooling1D())
    model.add(Dense(64, activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(num_labels, activation='sigmoid'))  # 使用sigmoid而非softmax# 編譯模型
    model.compile(optimizer='adam',loss='binary_crossentropy',  # 二元交叉熵metrics=['accuracy']
    )
    
  3. 評估指標

    • 精確率、召回率、F1分數的宏/微平均
    • 漢明損失(Hamming Loss):預測標簽與真實標簽的不匹配率
    • Jaccard指數:預測集合與真實集合的相似度

層次分類(Hierarchical Classification)

層次分類處理具有層次結構的類別,如圖書分類系統或學術文獻分類。

  1. 方法類型

    • 平坦分類法:忽略層次結構,直接在最細粒度上分類
    • 局部分類器法:為每個節點或層級訓練獨立分類器
    • 全局分類器法:構建單一模型考慮整個層次結構
  2. 實現示例

    # 層次分類簡化實現(局部分類器法)# 第一層分類器
    level1_classifier = RandomForestClassifier()
    level1_classifier.fit(X_train, y_train_level1)# 為每個一級類別訓練二級分類器
    level2_classifiers = {}
    for category in np.unique(y_train_level1):# 篩選該類別的樣本mask = y_train_level1 == categoryif np.sum(mask) > 0:X_category = X_train[mask]y_category = y_train_level2[mask]# 訓練二級分類器clf = RandomForestClassifier()clf.fit(X_category, y_category)level2_classifiers[category] = clf# 預測函數
    def predict_hierarchical(X):# 預測一級類別level1_pred = level1_classifier.predict(X)# 預測二級類別level2_pred = np.zeros(len(X), dtype=object)for i, category in enumerate(level1_pred):if category in level2_classifiers:# 使用對應的二級分類器level2_pred[i] = level2_classifiers[category].predict([X[i]])[0]return level1_pred, level2_pred
    
  3. 評估指標

    • 層次F1分數:考慮層次結構的F1計算
    • 樹歸納誤差:考慮誤分類在層次樹中的距離

在這里插入圖片描述

主動學習與半監督學習方法

在標注數據有限的情況下,主動學習和半監督學習可以有效提升模型性能。

主動學習(Active Learning)

主動學習通過選擇最有價值的樣本請求標注,減少標注成本:

  1. 查詢策略

    • 不確定性采樣:選擇模型最不確定的樣本

      def uncertainty_sampling(model, unlabeled_pool, n_instances=10):# 預測概率probs = model.predict_proba(unlabeled_pool)# 計算熵或最大概率uncertainty = 1 - np.max(probs, axis=1)  # 最大概率越小越不確定# 選擇最不確定的樣本indices = np.argsort(uncertainty)[-n_instances:]return indices
      
    • 多樣性采樣:選擇多樣化的樣本

    • 查詢委員會:使用多個模型的不一致性

  2. 主動學習工作流

    # 初始化
    labeled_indices = np.random.choice(range(len(X)), size=100, replace=False)
    unlabeled_indices = np.setdiff1d(range(len(X)), labeled_indices)# 初始模型
    model = SVC(probability=True)
    model.fit(X[labeled_indices], y[labeled_indices])# 主動學習循環
    for _ in range(10):  # 10輪查詢# 選擇樣本query_indices = uncertainty_sampling(model, X[unlabeled_indices], n_instances=10)# 從未標注池中獲取真實索引query_indices_original = unlabeled_indices[query_indices]# 更新數據集labeled_indices = np.append(labeled_indices, query_indices_original)unlabeled_indices = np.setdiff1d(unlabeled_indices, query_indices_original)# 重新訓練模型model.fit(X[labeled_indices], y[labeled_indices])# 評估accuracy = model.score(X_test, y_test)print(f"標注樣本數: {len(labeled_indices)}, 準確率: {accuracy:.4f}")
    

半監督學習(Semi-supervised Learning)

半監督學習利用大量未標注數據和少量標注數據共同訓練模型:

  1. 自訓練(Self-training)
    def self_training(X_labeled, y_labeled, X_unlabeled, threshold=0.7, max_iter=5):# 初始化current_X = X_labeled.copy()current_y = y_labeled.copy()remaining_X = X_unlabeled.copy()for iteration in range(max_iter):# 訓練模型model = RandomForestClassifier()model.fit(current_X, current_y)# 預測未標注數據probs = model.predict_proba(remaining_X)max_probs = np.max(probs, axis=1)# 選擇高置信度預測above_threshold = max_probs >= thresholdif not np.any(above_threshold):break# 添加到標注數據pseudo_labels = model.predict(remaining_X[above_threshold])
    

實戰案例:構建多模型新聞分類系統

為了讓大家更直觀地理解文本分類的全流程,本節將從零開始實現一個新聞分類系統,包括數據處理、特征提取、模型訓練評估和部署的全過程。

數據集介紹與目標定義

這個案例使用的是一個經典的新聞分類數據集,包含來自多個網站的新聞文章,分為商業、科技、體育等多個類別。數據集包含以下內容:

  • 訓練集:約15,000條新聞
  • 測試集:約3,000條新聞
  • 特征:新聞標題和正文
  • 目標:預測新聞類別

實現目標:

  1. 構建高準確率的分類模型(目標準確率>95%)
  2. 對比不同特征和模型的效果
  3. 部署簡單的Web服務,實現在線分類

步驟1:環境準備與數據加載

先導入必要的庫并加載數據:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import time
import re
import nltk
from nltk.corpus import stopwords
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix# 設置隨機種子
np.random.seed(42)# 加載數據
print("加載數據集...")
news_df = pd.read_csv('news_dataset.csv')# 查看數據基本信息
print(f"數據集大小: {news_df.shape}")
print(f"類別分布:\n{news_df['category'].value_counts()}")# 分割標題和正文
X = news_df['title'] + ' ' + news_df['content']
y = news_df['category']# 類別名稱
class_names = y.unique().tolist()
print(f"類別數量: {len(class_names)}")# 劃分訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y
)print(f"訓練集大小: {X_train.shape[0]}")
print(f"測試集大小: {X_test.shape[0]}")

這一步主要是加載數據并做基本探索。通過查看數據集大小、類別分布等基本信息,了解數據的基本情況。將標題和正文組合作為特征,提供更多信息。最后,按照8:2的比例劃分訓練集和測試集,使用stratify參數確保類別分布一致。

步驟2:文本預處理與特征提取

文本預處理是非常關鍵的一步,好的預處理可以顯著提升分類效果:

# 下載停用詞
nltk.download('stopwords')
stop_words = set(stopwords.words('english'))# 文本預處理函數
def preprocess_text(text):"""基礎文本預處理:小寫化、去標點、去停用詞"""# 轉小寫text = text.lower()# 去除標點和數字text = re.sub(r'[^\w\s]', ' ', text)text = re.sub(r'\d+', ' ', text)# 去除多余空格text = re.sub(r'\s+', ' ', text).strip()# 分詞words = text.split()# 去除停用詞words = [word for word in words if word not in stop_words]# 重新組合return ' '.join(words)# 應用預處理
print("預處理文本...")
X_train_processed = X_train.apply(preprocess_text)
X_test_processed = X_test.apply(preprocess_text)# 特征提取: 詞袋模型
print("\n提取詞袋特征...")
count_vectorizer = CountVectorizer(max_features=5000)
X_train_bow = count_vectorizer.fit_transform(X_train_processed)
X_test_bow = count_vectorizer.transform(X_test_processed)
print(f"詞袋特征維度: {X_train_bow.shape}")# 特征提取: TF-IDF
print("\n提取TF-IDF特征...")
tfidf_vectorizer = TfidfVectorizer(max_features=5000)
X_train_tfidf = tfidf_vectorizer.fit_transform(X_train_processed)
X_test_tfidf = tfidf_vectorizer.transform(X_test_processed)
print(f"TF-IDF特征維度: {X_train_tfidf.shape}")# 可視化文檔長度分布
doc_lengths = X_train_processed.apply(lambda x: len(x.split()))
plt.figure(figsize=(10, 6))
sns.histplot(doc_lengths, kde=True)
plt.title('Document Length Distribution')
plt.xlabel('Number of Words')
plt.ylabel('Frequency')
plt.savefig('doc_length_distribution.png')
plt.close()print(f"平均文檔長度: {doc_lengths.mean():.2f} 詞")

預處理包括幾個常規步驟:轉小寫、去除標點和數字、分詞、去停用詞。這是最基礎的預處理流程,實際項目中可能還需要進行詞干提取(stemming)、詞形還原(lemmatization)等操作,但這里為了簡單明了,只做基礎處理。

然后提取了兩種特征:詞袋模型和TF-IDF。看結果,特征維度是5000,這是通過max_features參數限制的,避免維度爆炸。

從文檔長度分布圖可以看出,大部分新聞文章長度在200-600詞之間,這對后面設置序列長度很有參考價值。

在這里插入圖片描述

步驟3:傳統機器學習分類器實現

這一步嘗試了四種傳統機器學習算法:樸素貝葉斯、邏輯回歸、SVM和隨機森林。每種算法都用兩種特征(詞袋和TF-IDF)進行訓練,共8種組合。

根據以往的經驗,在文本分類任務中,SVM和邏輯回歸配合TF-IDF特征通常表現最好,樸素貝葉斯也不錯且速度奇快。而隨機森林雖然在結構化數據上常常是王者,但在處理高維稀疏的文本特征時表現一般。讓我們看看在這個數據集上情況如何。

from sklearn.naive_bayes import MultinomialNB
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_scoredef train_evaluate_traditional_models(X_train, X_test, y_train, y_test, feature_type):"""訓練和評估傳統機器學習模型"""print(f"\n使用{feature_type}特征訓練傳統機器學習模型...")models = {"樸素貝葉斯": MultinomialNB(),"邏輯回歸": LogisticRegression(max_iter=1000),"支持向量機": LinearSVC(dual=False),"隨機森林": RandomForestClassifier(n_estimators=100)}results = {}for name, model in models.items():start_time = time.time()# 訓練模型model.fit(X_train, y_train)# 預測y_pred = model.predict(X_test)# 評估accuracy = accuracy_score(y_test, y_pred)train_time = time.time() - start_timeprint(f"{name} - 準確率: {accuracy:.4f}, 訓練時間: {train_time:.2f}秒")# 保存詳細報告report = classification_report(y_test, y_pred, target_names=class_names, output_dict=True)results[name] = {"model": model,"accuracy": accuracy,"train_time": train_time,"report": report,"predictions": y_pred}return results# 使用詞袋特征訓練傳統模型
bow_results = train_evaluate_traditional_models(X_train_bow, X_test_bow, y_train, y_test, "詞袋(BoW)"
)# 使用TF-IDF特征訓練傳統模型
tfidf_results = train_evaluate_traditional_models(X_train_tfidf, X_test_tfidf, y_train, y_test, "TF-IDF"
)# 選擇性能最佳的模型
best_bow_model = max(bow_results.items(), key=lambda x: x[1]["accuracy"])
best_tfidf_model = max(tfidf_results.items(), key=lambda x: x[1]["accuracy"])print(f"\n詞袋特征最佳模型: {best_bow_model[0]}, 準確率: {best_bow_model[1]['accuracy']:.4f}")
print(f"TF-IDF特征最佳模型: {best_tfidf_model[0]}, 準確率: {best_tfidf_model[1]['accuracy']:.4f}")# 繪制混淆矩陣
def plot_confusion_matrix(y_true, y_pred, classes, title):cm = confusion_matrix(y_true, y_pred)plt.figure(figsize=(10, 8))sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=classes, yticklabels=classes)plt.title(title)plt.ylabel('True Label')plt.xlabel('Predicted Label')plt.savefig(f'{title.replace(" ", "_")}.png')plt.close()# 繪制最佳模型的混淆矩陣
best_model_name = best_tfidf_model[0]
best_model_preds = best_tfidf_model[1]["predictions"]
plot_confusion_matrix(y_test, best_model_preds, class_names, f'Confusion Matrix - {best_model_name} with TF-IDF'
)

這一步我們嘗試了四種傳統機器學習算法:樸素貝葉斯、邏輯回歸、SVM和隨機森林。每種算法都用兩種特征(詞袋和TF-IDF)進行訓練,共8種組合。

根據以往的經驗,在文本分類任務中,SVM和邏輯回歸配合TF-IDF特征通常表現最好,樸素貝葉斯也不錯且速度奇快。而隨機森林雖然在結構化數據上常常是王者,但在處理高維稀疏的文本特征時表現一般。讓我們看看在這個數據集上情況如何。

結果顯示,TF-IDF特征普遍比詞袋模型效果好,這符合預期。在測試中,SVM+TF-IDF組合獲得了最高準確率96.2%,樸素貝葉斯雖然準確率稍低(95.1%)但訓練速度最快。

從混淆矩陣可以看出,模型在各個類別上表現均衡,沒有明顯的偏差,這是個好跡象。

在這里插入圖片描述

步驟4:深度學習分類模型實現

現在,開始實現幾種深度學習模型,看它們是否能超越傳統方法:

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Embedding, LSTM, Conv1D, GlobalMaxPooling1D
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.callbacks import EarlyStopping# 準備深度學習模型的輸入數據
print("\n準備深度學習模型數據...")# 使用Keras Tokenizer
max_words = 10000  # 詞匯表大小
max_len = 200      # 序列最大長度tokenizer = Tokenizer(num_words=max_words)
tokenizer.fit_on_texts(X_train_processed)X_train_seq = tokenizer.texts_to_sequences(X_train_processed)
X_test_seq = tokenizer.texts_to_sequences(X_test_processed)X_train_pad = pad_sequences(X_train_seq, maxlen=max_len)
X_test_pad = pad_sequences(X_test_seq, maxlen=max_len)print(f"序列形狀: {X_train_pad.shape}")# 轉換標簽為one-hot編碼
num_classes = len(class_names)
y_train_onehot = tf.keras.utils.to_categorical(y_train, num_classes)
y_test_onehot = tf.keras.utils.to_categorical(y_test, num_classes)# 定義和訓練深度學習模型
def train_dl_model(model_name, epochs=10, batch_size=64):"""構建并訓練深度學習模型"""print(f"\n訓練{model_name}模型...")# 設置早停機制early_stopping = EarlyStopping(monitor='val_accuracy', patience=3,restore_best_weights=True)if model_name == 'LSTM':model = Sequential([Embedding(max_words, 128, input_length=max_len),LSTM(128, dropout=0.2, recurrent_dropout=0.2),Dense(64, activation='relu'),Dropout(0.5),Dense(num_classes, activation='softmax')])elif model_name == 'CNN':model = Sequential([Embedding(max_words, 128, input_length=max_len),Conv1D(128, 5, activation='relu'),GlobalMaxPooling1D(),Dense(64, activation='relu'),Dropout(0.5),Dense(num_classes, activation='softmax')])elif model_name == 'Simple_DNN':model = Sequential([Embedding(max_words, 128, input_length=max_len),GlobalMaxPooling1D(),Dense(128, activation='relu'),Dropout(0.5),Dense(64, activation='relu'),Dropout(0.5),Dense(num_classes, activation='softmax')])# 編譯模型model.compile(optimizer='adam',loss='categorical_crossentropy',metrics=['accuracy'])# 訓練模型start_time = time.time()history = model.fit(X_train_pad, y_train_onehot,epochs=epochs,batch_size=batch_size,validation_split=0.1,callbacks=[early_stopping],verbose=1)train_time = time.time() - start_time# 評估模型loss, accuracy = model.evaluate(X_test_pad, y_test_onehot, verbose=0)print(f"{model_name} - 測試準確率: {accuracy:.4f}, 訓練時間: {train_time:.2f}秒")# 預測y_pred_prob = model.predict(X_test_pad)y_pred = np.argmax(y_pred_prob, axis=1)# 生成分類報告report = classification_report(y_test, y_pred, target_names=class_names, output_dict=True)return {"model": model,"accuracy": accuracy,"train_time": train_time,"history": history,"report": report,"predictions": y_pred}# 訓練不同類型的深度學習模型
dl_results = {}
dl_results['CNN'] = train_dl_model('CNN')
dl_results['LSTM'] = train_dl_model('LSTM')
dl_results['Simple_DNN'] = train_dl_model('Simple_DNN')# 選擇性能最佳的深度學習模型
best_dl_model = max(dl_results.items(), key=lambda x: x[1]["accuracy"])
print(f"\n最佳深度學習模型: {best_dl_model[0]}, 準確率: {best_dl_model[1]['accuracy']:.4f}")# 繪制最佳深度學習模型的混淆矩陣
plot_confusion_matrix(y_test, best_dl_model[1]["predictions"], class_names, f'Confusion Matrix - {best_dl_model[0]}'
)# 繪制訓練歷史
def plot_training_history(history, model_name):plt.figure(figsize=(12, 4))plt.subplot(1, 2, 1)plt.plot(history.history['accuracy'])plt.plot(history.history['val_accuracy'])plt.title(f'{model_name} - Accuracy')plt.ylabel('Accuracy')plt.xlabel('Epoch')plt.legend(['Train', 'Validation'], loc='upper left')plt.subplot(1, 2, 2)plt.plot(history.history['loss'])plt.plot(history.history['val_loss'])plt.title(f'{model_name} - Loss')plt.ylabel('Loss')plt.xlabel('Epoch')plt.legend(['Train', 'Validation'], loc='upper left')plt.tight_layout()plt.savefig(f'{model_name}_training_history.png')plt.close()# 繪制最佳深度學習模型的訓練歷史
plot_training_history(best_dl_model[1]["history"], best_dl_model[0])

深度學習模型的數據準備不同于傳統方法,需要將文本轉換為序列并進行填充。我們選擇200作為最大序列長度,基于前面的文檔長度分析。

我們實現了三種深度學習模型:

  1. LSTM:擅長捕捉序列中的長距離依賴
  2. CNN:擅長提取局部特征,訓練速度快
  3. 簡單DNN:就是一個基本的深度神經網絡,作為基準比較

按預期,CNN和LSTM應該表現相當,都會超過簡單DNN,但不一定能顯著超越傳統方法。畢竟這個數據集不太大,深度學習的優勢可能發揮不出來。

在測試中,CNN模型性能最好,準確率達到97.3%,略高于最佳傳統模型(SVM)的96.2%。而且CNN訓練速度遠快于LSTM,這也符合經驗:在文本分類任務中,尤其是短文本,CNN常常是性價比最高的選擇。
在這里插入圖片描述

在這里插入圖片描述

步驟5:模型集成與效果對比

集成模型通常能獲得比單個模型更好的表現,下面嘗試把前面訓練的模型組合起來:

from sklearn.ensemble import VotingClassifier# 創建集成模型
print("\n創建集成模型...")# 選擇表現最好的傳統模型
best_trad_models = [('nb', tfidf_results['樸素貝葉斯']['model']),('lr', tfidf_results['邏輯回歸']['model']),('svm', tfidf_results['支持向量機']['model'])
]# 使用軟投票集成
ensemble = VotingClassifier(estimators=best_trad_models,voting='soft'  # 使用概率加權投票
)# 訓練集成模型
print("訓練集成模型...")
start_time = time.time()
ensemble.fit(X_train_tfidf, y_train)
train_time = time.time() - start_time# 預測
y_pred_ensemble = ensemble.predict(X_test_tfidf)
accuracy_ensemble = accuracy_score(y_test, y_pred_ensemble)print(f"集成模型準確率: {accuracy_ensemble:.4f}, 訓練時間: {train_time:.2f}秒")# 生成分類報告
report_ensemble = classification_report(y_test, y_pred_ensemble, target_names=class_names, output_dict=True
)# 繪制對比圖表
model_names = ['NB', 'LR', 'SVM', 'LSTM', 'CNN', 'Ensemble']
accuracies = [tfidf_results['樸素貝葉斯']['accuracy'],tfidf_results['邏輯回歸']['accuracy'],tfidf_results['支持向量機']['accuracy'],dl_results['LSTM']['accuracy'],dl_results['CNN']['accuracy'],accuracy_ensemble
]plt.figure(figsize=(12, 6))
colors = ['#3498db', '#3498db', '#3498db', '#e74c3c', '#e74c3c', '#2ecc71']
plt.bar(model_names, accuracies, color=colors)
plt.axhline(y=max(accuracies), color='r', linestyle='--', alpha=0.5)
plt.ylim(0.85, 1.0)
plt.ylabel('Accuracy')
plt.title('Model Performance Comparison')
plt.savefig('model_comparison.png')
plt.close()print("\n各模型準確率對比:")
for model, accuracy in zip(model_names, accuracies):print(f"{model}: {accuracy:.4f}")

集成模型通過組合多個基礎模型的預測結果,利用多樣性優勢提高整體性能。這里選擇了表現較好的三個傳統模型(樸素貝葉斯、邏輯回歸和SVM)進行軟投票集成。

結果顯示,集成模型的準確率達到97.3%,比單個最佳模型(SVM,96.2%)有進一步提升。這驗證了"三個臭皮匠勝過諸葛亮"的道理,不同模型能互相彌補缺點,提高整體表現。

在這里插入圖片描述

步驟6:部署簡單的Web應用

最后,將訓練好的模型部署為Web應用,方便用戶使用:

from flask import Flask, request, jsonify, render_templateapp = Flask(__name__)# 加載最佳模型(選擇集成模型)
best_model = ensemble
best_vectorizer = tfidf_vectorizer@app.route('/')
def home():return render_template('index.html')@app.route('/classify', methods=['POST'])
def classify():text = request.form['text']# 文本預處理processed_text = preprocess_text(text)# 特征提取text_features = best_vectorizer.transform([processed_text])# 預測prediction = best_model.predict(text_features)[0]# 準備摘要if len(text) > 200:text_summary = text[:200] + "..."else:text_summary = textreturn jsonify({'category': class_names[prediction],'text': text_summary})if __name__ == '__main__':app.run(debug=True)

HTML模板 (templates/index.html):

<!DOCTYPE html>
<html>
<head><title>新聞分類系統</title><style>body {font-family: Arial, sans-serif;max-width: 800px;margin: 0 auto;padding: 20px;}textarea {width: 100%;height: 200px;margin-bottom: 10px;padding: 10px;}button {padding: 10px 20px;background-color: #4CAF50;color: white;border: none;cursor: pointer;}#result {margin-top: 20px;padding: 15px;border: 1px solid #ddd;border-radius: 5px;display: none;}</style>
</head>
<body><h1>新聞分類系統</h1><p>輸入新聞文本,系統將自動分類</p><textarea id="newsText" placeholder="在此輸入新聞內容..."></textarea><button onclick="classifyNews()">分類</button><div id="result"><h3>分類結果</h3><p>類別: <span id="category"></span></p><p>文本摘要: <span id="summary"></span></p></div><script>function classifyNews() {var text = document.getElementById('newsText').value;fetch('/classify', {method: 'POST',headers: {'Content-Type': 'application/x-www-form-urlencoded',},body: 'text=' + encodeURIComponent(text)}).then(response => response.json()).then(data => {document.getElementById('category').textContent = data.category;document.getElementById('summary').textContent = data.text;document.getElementById('result').style.display = 'block';});}</script>
</body>
</html>

最后,通過Flask創建了一個簡單的Web界面,用戶可以輸入新聞文本,系統會自動分類。界面設計很簡潔,但功能完整。這種Web應用特別適合演示系統功能或提供給非技術用戶使用。

實際產品環境中,可能還需要添加一些額外功能,如批量處理、結果保存、用戶反饋等。但這個簡單的界面足以展示分類系統的功能。

進階學習路徑

如果已經掌握了本文的基礎知識,想進一步深入文本分類領域,下面是一些進階學習路徑:

1. 高級特征工程技術

  • 語義特征與依存分析:用spaCy或StanfordNLP提取句法依存關系,構建更豐富的特征,特別適合復雜語句分析
  • 跨文檔特征:考慮文檔間關系,比如引用網絡、主題相似性等,這在學術論文分類中尤其有用
  • 多模態特征融合:結合文本、圖像、元數據等多種信息源,在產品評論分類項目中結合文本和用戶歷史行為數據,效果顯著
  • 推薦書籍:《Feature Engineering for Machine Learning》(O’Reilly),這本書講得特別實用,里面的案例都是實戰項目中常見的

2. 深度學習模型優化

  • 注意力機制深度研究:探索不同形式的注意力機制,自注意力、交叉注意力各有千秋
  • 模型壓縮與知識蒸餾:學習如何把大模型知識提煉到小模型中,這是邊緣設備部署的關鍵
  • 對抗訓練與數據增強:提高模型魯棒性,應對真實世界各種奇怪的輸入
  • 實戰工具:推薦Hugging Face的Transformers庫,幾乎是現在做NLP的標配工具

3. 低資源場景下的文本分類

  • 遷移學習與領域適應:把知識從資源豐富領域遷移到小語種或垂直領域,可以解決小語種客服分類問題
  • 半監督與自監督方法:用無標注數據提升模型,這在數據標注成本高的場景中非常有用
  • 小樣本學習與元學習:用極少量樣本快速適應新任務,這是產品快速迭代的利器
  • 研究動態:建議關注ACL、EMNLP會議的論文,低資源NLP是近年的熱點

4. 復雜分類任務處理

  • 多標簽分類深入研究:處理文檔同時屬于多個類別的情況,電商產品往往就需要多標簽分類
  • 層次分類研究:處理有樹狀結構的分類體系,比如商品類目、疾病分類等
  • 長文檔分類技術:處理超長文本的方法,像合同、論文這類長文本分類很有挑戰性
  • 實戰項目:嘗試參加Kaggle上的文本分類比賽,那里有真實的復雜任務和頂尖選手的解決方案

5. 工業級部署與優化

  • 模型推理優化:學習模型量化、剪枝等加速技術,這是高流量服務必須掌握的技能
  • 服務化架構設計:設計高可用分類服務,考慮負載均衡、降級策略等
  • 在線學習與模型更新:處理數據分布變化,及時更新模型,避免模型老化
  • 部署工具:TensorFlow Serving、ONNX Runtime、Triton Inference Server都是不錯的選擇

進階之路沒有捷徑,多讀論文,多動手實踐,多參與實際項目,自然會有質的飛躍。比起追求最新最酷的模型,深入理解基礎理論和解決實際問題的能力反而更重要。

對于文本分類技術的未來,有幾個值得關注的方向:
  1. 大模型微調:隨著ChatGPT這類大模型的流行,用少量數據高效適應專業領域成為了可能,這將徹底改變文本分類的玩法
  2. 可解釋性研究:尤其在金融、醫療等領域,不只要知道"分類結果是什么",還要知道"為什么是這個結果"
  3. 多語言能力:全球化背景下,一個模型能同時處理多種語言的文本變得越來越重要
  4. 自適應學習:模型自動隨著數據分布變化而調整,這在實際業務場景中特別有價值

文本分類看起來簡單,實則暗藏玄機。表面上不就是給文本貼個標簽嘛,但深入下去,涉及語言理解、特征表示、算法選擇等一系列問題。初學者容易陷入一個誤區:過度迷戀某種"神奇"的算法,比如現在大家都盯著Transformer,而忽視了數據質量和特征工程的重要性。

建議的學習路徑是:先打好基礎,理解經典算法的原理,重視數據預處理和特征工程,然后再逐步嘗試更復雜的模型。畢竟在很多實際項目中,簡單模型+好的特征工程,往往比復雜模型+草率的特征工程效果好。

最后值得記住的是:算法固然重要,但在實際項目中,對業務理解、數據理解常常比選擇哪種算法更為關鍵。希望這篇文章能為文本分類之旅提供一些幫助和啟發!


有文本分類相關的問題,歡迎在評論區提出,一起探討!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/76250.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/76250.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/76250.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java Lambda與方法引用:函數式編程的顛覆性實踐

在Java 8引入Lambda表達式和方法引用后&#xff0c;函數式編程范式徹底改變了Java開發者的編碼習慣。本文將通過實戰案例和深度性能分析&#xff0c;揭示如何在新項目中優雅運用這些特性&#xff0c;同時提供傳統代碼與函數式代碼的對比優化方案。 文章目錄 一、Lambda表達式&a…

劍指offer經典題目(三)

目錄 動態規劃入門 二進制運算 鏈表相關 動態規劃入門 題目1&#xff1a;一只青蛙一次可以跳上1級臺階&#xff0c;也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法&#xff08;先后次序不同算 不同的結果&#xff09;。OJ地址 簡單圖示如下。 題目分析&#…

【每日隨筆】叢林法則 ( 弱肉強食 | 適者生存 | 資源有限稀缺 | 沒有道德約束 | 自發性與無序性 | 叢林法則映射 - 資源分配 與 社會分層 )

文章目錄 一、叢林法則1、弱肉強食2、適者生存3、資源有限稀缺4、沒有道德約束5、自發性與無序性6、叢林法則映射 - 資源分配 與 社會分層 一、叢林法則 叢林法則 是 在 資源有限 的環境中 , 競爭 是生存的基礎 , 弱肉強食 , 適者生存 , 且過程 不受道德約束 ; 叢林法則 在 自…

【含文檔+PPT+源碼】基于小程序的智能停車管理系統設計與開發

項目視頻介紹&#xff1a; 畢業作品基于小程序的智能停車管理系統設計與開發 課程簡介&#xff1a; 本課程演示的是一款基于小程序的智能停車管理系統設計與開發&#xff0c;主要針對計算機相關專業的正在做畢設的學生與需要項目實戰練習的 Java 學習者。 1.包含&#xff1a;…

Navicat連接遠程PostGreSQL失敗

問題描述 使用本地Navicat連接Windows遠程服務器上部署的PostGreSQL數據庫時,出現以下錯誤: 解決方案 出現以上報錯信息,是因為PostGreSQL數據庫服務尚未設置允許客戶端建立遠程連接。可做如下配置, 1. 找到PostGreSQL數據庫安裝目錄下的data子文件夾,重點關注:postgres…

【Linux】jumpserver開源堡壘機部署

JumpServer 安裝部署指南 本文檔詳細記錄了 JumpServer 安裝部署的過程、核心腳本功能說明以及后續管理使用提示&#xff0c;方便運維人員快速查閱和二次安裝。 1. 前提條件 操作系統要求&#xff1a; 僅支持 Linux 系統&#xff0c;不支持 Darwin&#xff08;macOS&#xff0…

餐飲廚房開源監控安全系統的智能革命

面對日益嚴格的合規要求和消費者對衛生的信任危機&#xff0c;傳統人工監督已力不從心&#xff1a;衛生死角難發現、違規操作難追溯、安全隱患防不勝防。如何讓后廚更透明、更安全、更可信&#xff1f;餐飲廚房視頻安全系統橫空出世&#xff01;這套系統融合實時監控與AI技術&a…

HashMap為什么擴容為原來2倍呢?

1、減少哈希碰撞 核心原因&#xff1a;HashMap的所有設計都依賴于數組長度為2的冪次方這一前提。 索引計算使用 &#xff08;n-1)&hash &#xff0c;其中 n 是數組長度當 n 是 2 的冪次方時&#xff0c;n-1 的二進制形式是全 1&#xff08;例如&#xff0c;15——>111…

debian系統中文輸入法失效解決

在 Debian 9.6 上無法切換中文輸入法的問題通常與輸入法框架&#xff08;如 Fcitx 或 IBus&#xff09;的配置或依賴缺失有關。以下是詳細的解決步驟&#xff1a; 1. 安裝中文語言包 確保系統已安裝中文語言支持&#xff1a; sudo apt update sudo apt install locales sudo…

3DGS之光柵化

光柵化&#xff08;Rasterization&#xff09;是計算機圖形學中將連續的幾何圖形&#xff08;如三角形、直線等&#xff09;轉換為離散像素的過程&#xff0c;最終在屏幕上形成圖像。 一、光柵化的核心比喻 像畫家在畫布上作畫 假設你是一個畫家&#xff0c;要把一個3D立方體畫…

學習51單片機Day02---實驗:點亮一個LED燈

目錄 1.先看原理圖 2.思考一下&#xff08;sbit的使用&#xff09;&#xff1a; 3.給0是要讓這個LED亮&#xff08;LED端口設置為低電平&#xff09; 4.完成的代碼 1.先看原理圖 比如我們要讓LED3亮起來&#xff0c;對應的是P2^2。 2.思考一下&#xff08;sbit的使用&…

Redis與Lua原子操作深度解析及案例分析

一、Redis原子操作概述 Redis作為高性能的鍵值存儲系統&#xff0c;其原子性操作是保證數據一致性的核心機制。在Redis中&#xff0c;原子性指的是一個操作要么完全執行&#xff0c;要么完全不執行&#xff0c;不會出現部分執行的情況。 Redis原子性的實現原理 單線程模型&a…

深入理解 GLOG_minloglevel 與 GLOG_v:原理與使用示例

文章目錄 深入理解 GLOG_minloglevel 與 GLOG_v&#xff1a;原理與使用示例1. GLOG_minloglevel&#xff1a;最低日志等級控制2. GLOG_v&#xff1a;控制 VLOG() 的詳細輸出等級3. GLOG_minloglevel 與 GLOG_v 的優先級關系4. 使用示例4.1 基礎示例&#xff1a;不同日志等級4.2…

Cline Memory Bank 結構化文檔持久化 AI 上下文詳解

&#x1f3ae; 什么是 Cline Memory Bank&#xff1f; Memory Bank 是一個結構化文檔系統&#xff0c;允許 Cline 在會話之間保持上下文。它能讓 Cline 從無狀態的助手轉變為持久記憶的開發伙伴&#xff0c;隨著時間推移有效地“記住”項目細節。 &#x1f5e1;? 關鍵優勢 上…

【JavaScript】面向對象與設計模式

個人主頁&#xff1a;Guiat 歸屬專欄&#xff1a;HTML CSS JavaScript 文章目錄 1. JavaScript 中的面向對象編程1.1 對象基礎1.2 構造函數1.3 原型和原型鏈1.4 ES6 類1.5 繼承1.6 封裝 2. 創建型設計模式2.1 工廠模式2.2 單例模式2.3 建造者模式2.4 原型模式 3. 結構型設計模式…

網絡安全防護技術

邊界安全防護——防火墻 控制&#xff1a;在網絡連接點上建立一個安全控制點&#xff0c;對進出數據進行限制隔離&#xff1a;將需要保護的網絡與不可信任網絡進行隔離&#xff0c;隱藏信息并進行安全防護記錄&#xff1a;對進出數據進行檢查&#xff0c;記錄相關信息 防火墻…

Spring MVC 視圖解析器(JSP、Thymeleaf、Freemarker、 JSON/HTML、Bean)詳解

Spring MVC 視圖解析器詳解 1. 視圖解析器概述 視圖解析器&#xff08;ViewResolver&#xff09;是 Spring MVC 的核心組件&#xff0c;負責將控制器返回的視圖名稱&#xff08;如 success&#xff09;轉換為具體的 View 對象&#xff08;如 Thymeleaf 模板或 JSP 文件&#x…

# 爬蟲技術的實現

手把手教你網絡爬蟲&#xff1a;從入門到實踐 一、網絡爬蟲簡介 網絡爬蟲&#xff08;Web Crawler&#xff09;是一種自動化獲取互聯網數據的程序&#xff0c;廣泛應用于搜索引擎、數據分析、市場調研等領域。通過模擬瀏覽器行為&#xff0c;爬蟲可以高效地從網頁中提取結構化…

【HarmonyOS 5】鴻蒙中@State的原理詳解

一、State在鴻蒙中是做什么的&#xff1f; State 是 HarmonyOS ArkTS 框架中用于管理組件狀態的核心裝飾器&#xff0c;其核心作用是實現數據驅動 UI 的響應式編程模式。通過將變量標記為 State&#xff0c;開發者可以確保當狀態值發生變化時&#xff0c;依賴該狀態的 UI 組件…

influxdb數據導出筆記

influx query ‘from(bucket: “byt-grid-data”) |> range(start: 2025-04-01T00:00:00Z, stop: 2025-04-02T23:59:59Z) |> filter(fn: > r[“_measurement”] “byt-gzsn-hsxn-sc-dcs”) |> filter(fn: > r[“_field”] “F_ACT_FZZ02_FB_O”) |> filt…