機器學習——“讓樹幫你做決策”之決策樹(Decision Tree)(ID3,C4.5,CART,決策樹剪枝)

引入:去不去相親?

我們現實生活中經常見到自己或者其他人做許多決策:今天是否去打球?明天要不要逃課?家人介紹的相親對象要不要去見?
我們以最後一個爲例:
比如說小好媽媽對小好說:“我同事家一男孩兒還不錯,你週末去見見。”
小好問:“他多大了?超過30我不見”
媽媽:“沒有,和你同歲”
小好:“長得帥不帥?低於一般水平我不見”
媽媽:“長得可帥了,像古天樂呢!”
小好:“O(∩_∩)O哈哈~那他收入怎樣?”
(PS.emmm…像古天樂肯定就去啦,但是,爲了例子的完整性,還是要象徵性的再問一個問題~)
媽媽:“一般般”
小好:“是公務員嗎?”
媽媽:“是的”
小好:“那我去見~”
上述對話可用決策樹表示爲:
在這裏插入圖片描述
這就是決策樹比較簡單的表現形式,用樹的形式更直觀的展現決策流程。
可能有人會問:這我幹嘛要做成樹的形式,我不用樹也可以清楚的知道結果。但是這是針對很小樣本的情況,如果樣本量很大,或者小好的要求很零散、很多,這時不用決策樹很難一目瞭然的得到一般規律和結果。
這就是決策樹算法存在的意義,那麼決策樹算法到底是什麼?讓我們來推開決策樹的大門!

決策樹(Decision Tree)

定義

決策樹(Decision Tree),又稱爲判定樹,是數據挖掘技術中的一種重要的分類方法,它是一種以樹結構(包括二叉樹和多叉樹)形式來表達的預測分析模型。

決策樹方法是一種比較通用的分類函數逼近法,它是一種常用於預測模型的算法,通過將大量數據有目的分類,找到一些有潛在價值的信息
具體用到的知識我們下一步說,先來看看決策樹的構造。

決策樹的構造

就像上圖是否去相親的決策樹,其中綠色節點表示判斷條件橙色節點表示決策結果箭頭表示在一個判斷條件在不同情況下的決策路徑,圖中紅色箭頭表示了上面例子中女孩的決策過程。
由上可知,決策樹需要有內部節點:判別條件(年齡、長相、收入、是否是公務員);葉節點:決策結果(是否去相親);分支:不同判別條件下的決策路徑;而根節點就是樹最頂層的節點,可以理解爲最先的分裂屬性,也是所有內部節點的父節點。
Note.什麼是父子節點?
由上述圖形,年齡節點是長相節點的父節點,也是根節點,子節點由父節點根據某分類規則得來。{決策樹的樹結構\begin{cases}根節點\\內部節點\\葉節點\end{cases}

決策樹算法原理

在介紹決策樹算法原理之前,需要了解一點:決策樹的構建準則是使信息量以最快的速度降低到0。
那麼,什麼是信息量?如何衡量信息量?

在信息論中,認爲信源輸出的消息是隨機的。即在未收到消息之前,是不能肯定信源到底發送什麼樣的消息。而通信的目的也就是要使接收者在接收到消息後,儘可能多的解除接收者對信源所存在的疑義(不定度),因此這個被解除的不定度實際上就是在通信中所要傳送的信息量。”

簡而言之,信息量是用來描述信息的不確定程度。比如說,'太陽東昇西落’這件事是必然事件,對於這種大家都已知的必然事件帶來的信息量就爲0,因爲沒有消除大家的任何疑慮,也就是說這個信息不存在任何不確定性程度的變動(因爲他就是確定的,必然發生的)。
再比如:專業射擊選手小王(成績平均在9.0)和射擊新手小明(成績平均在5.0)分別進行射擊,對於觀衆來說,小王射擊這個事件的不確定性較小(通常在9.0附近),而小明射擊的不確定性較大(可能時好時壞),所以可以認爲小明射擊事件的信息量更大。
那麼信息量是用什麼來衡量的呢?就像我們剛纔舉的例子,“信息的不確定程度的變化”,我們使用信息的不確定程度的降低來衡量信息量的大小。
由於上述的例子中,必然事件(概率爲1)的信息量最小,爲零。不確定性越大的事件(小明射擊)的信息量較大。因此,信息量和概率有着必然的聯繫。
信息量的公式爲:h(x)=log2p(x)h(x)=-log_2p(x)
信息熵的公式爲:H(X)=iNpi(xi)log(pi(xi))H(X)=-\sum_i^N p_i(x_i)log(p_i(x_i))
信息熵:信息對可能產生的信息量的期望——信息量的所有可能取值,即所有可能發生事件所帶來的信息量的期望。
注:信息熵用來衡量事件不確定性。其值越大,不確定性越高。
回到這句話:決策樹的構建準則是使信息量以最快的速度降低到0
可見,決策樹的構建過程是一種優化思想:確定性增益最大化!(將‘不確定性’看做‘不純度’,‘確定性’看做‘純度’)即決策樹構建要使純度增益最大化。
綜上可得,決策樹的算法原理:根據不純度函數,使每一次選擇分裂屬性帶來的數據不純度下降最大(純度增益最大化)。
那麼,決策樹算法的重點就到了不純度函數分裂屬性的選擇上。

決策樹算法

根據決策樹的發展史,經典算法的演變和發展分別爲:ID3——>C4.5——>CART,本文主要介紹這三個算法。
對於不同方法的分裂屬性的選擇方法先總結至下表:

算法 分裂屬性度量
ID3 信息增益
C4.5 信息增益率
CART 基尼係數

下面,我們根據一個例子,分別詳細講解ID3,C4.5,CART算法的分類過程。
例:根據日誌密度,好友密度、是否使用真實頭像、來判斷賬戶是否真實,如下是10條數據(無實意)

日誌密度 好友密度 是否使用真實頭像 賬戶是否真實
s l yes yes
m l no yes
l m yes yes
m m yes yes
l m yes yes
l m no yes
s s no no
m s no no
m s no yes
s s yes no

下面通過計算來深入瞭解每一個算法。

(1)ID3算法

1、原理介紹:若選擇一個屬性,使數據分類效果最好,其產生的信息增益最大。(因此,ID3算法以信息增益最大化來選擇分裂屬性)
對信息增益的理解——信息增益:若原數據信息熵爲I0I_0,根據屬性A劃分後得到信息熵I1I_1,I2I_2;信息增益爲:I0(I1+I2)I_0-(I_1+I_2)
首先,介紹ID3算法的計算公式:
總信息熵:H(D)=i=1cp(xi)logp(xi)H(D)=-\sum^c_{i=1}p(x_i)logp(x_i)某屬性分裂後的信息熵:H(DA)=j=1mDjDi=1cp(xi)logp(xi)H(D|分裂屬性A)=\sum_{j=1}^m \frac{|D_j|}{|D|}*\sum^c_{i=1}p(x_i)logp(x_i)信息增益:gain(A)=H(D)H(DA)gain(分裂屬性A)=H(D)-H(D|分裂屬性A)
2、案例計算,
信息熵的計算——
賬戶是否真實:{yes:7no:3\begin{cases}yes:7\\no:3\end{cases}
目標變量的信息熵:H(D)=710log2710310log2310=0.8813H(D)=-\frac{7}{10}log_2\frac{7}{10}-\frac{3}{10}log_2\frac{3}{10}=0.8813
①日誌密度:
在這裏插入圖片描述
H(D)=310[13log21323log223]+410[14log21434log234]+310[33log2330]=0.6H(D|日誌密度)=\frac{3}{10} [-\frac{1}{3}log_2 \frac{1}{3}-\frac{2}{3}log_2\frac{2}{3}]+\frac{4}{10} [-\frac{1}{4}log_2 \frac{1}{4}-\frac{3}{4}log_2\frac{3}{4}]+\frac{3}{10} [-\frac{3}{3}log_2 \frac{3}{3}-0]=0.6gain()=H(D)H(D)=0.88130.6=0.2813gain(日誌密度)=H(D)-H(D|日誌密度)=0.8813-0.6=0.2813
②好友密度:
在這裏插入圖片描述
H(D)=410[14log21434log234]+410[44log2440]+210[22log2220]=0.3245H(D|好友密度)=\frac{4}{10} [-\frac{1}{4}log_2 \frac{1}{4}-\frac{3}{4}log_2\frac{3}{4}]+\frac{4}{10} [-\frac{4}{4}log_2 \frac{4}{4}-0]+\frac{2}{10} [-\frac{2}{2}log_2 \frac{2}{2}-0]=0.3245 gain()=H(D)H(D)=0.88130.3245=0.5568gain(好友密度)=H(D)-H(D|好友密度)=0.8813-0.3245=0.5568
③是否使用真實頭像:
在這裏插入圖片描述
H(D使)=510[15log21545log245]+510[35log23525log225]=0.8464H(D|是否使用真實頭像)=\frac{5}{10} [-\frac{1}{5}log_2 \frac{1}{5}-\frac{4}{5}log_2\frac{4}{5}]+\frac{5}{10} [-\frac{3}{5}log_2 \frac{3}{5}-\frac{2}{5}log_2\frac{2}{5}]=0.8464 gain(使)=H(D)H(D使)=0.88130.8464=0.0349gain(是否使用真實頭像)=H(D)-H(D|是否使用真實頭像)=0.8813-0.8464=0.0349
gain()>gain()>gain(使)gain(好友密度)>gain(日誌密度)>gain(是否使用真實頭像)因此,首先選擇好友密度作爲分裂屬性,後面步驟相同,本文略。
在這裏插入圖片描述
3、ID3算法缺陷
對於ID3算法,若我們將數據增加一列ID:1,2,3,4,5,6,7,8,9,10
此時,H(DID)=j=1mDjDi=1cp(xi)logp(xi)=110[11log2110]+...+110[11log2110]]=0H(D|ID)=\sum_{j=1}^m \frac{|D_j|}{|D|}*\sum^c_{i=1}p(x_i)logp(x_i)=\frac{1}{10} [-\frac{1}{1}log_2 \frac{1}{1}-0]+...+\frac{1}{10} [-\frac{1}{1}log_2 \frac{1}{1}-0]]=0 gain(ID)=H(D)H(DID)=0.88130=0.8813gain(ID)=H(D)-H(D|ID)=0.8813-0=0.8813此時信息增益總是最大的,而我們知道按照ID劃分是沒有意義的。由此,我們得出ID3算法的一個缺陷:偏向於選擇多值屬性!
爲了解決這個問題,C4.5算法在其基礎上做出了改進,選擇分裂信息量來消除屬性取值越多信息增益越大的缺陷。

(2)C4.5算法

1、算法原理:引入分裂信息熵splitInf來消除信息增益的計算缺陷(若一個屬性的取值較多,會使Gain變大,splitinf也會越大,相互抵消。),選擇信息增益率越大的屬性作爲分裂屬性。
對信息增益率的理解——信息增益率:若原數據信息熵爲I0I_0,根據屬性A劃分後得到信息熵I1I_1,I2I_2,計算得到每個劃分的分裂信息熵splitInfsplitInf;信息增益率爲:[I0(I1+I2)]/splitInf[I_0-(I_1+I_2)]/splitInf
首先,介紹ID3算法的計算公式:
總信息熵:H(D)=i=1cp(xi)logp(xi)H(D)=-\sum^c_{i=1}p(x_i)logp(x_i)某屬性分裂後的信息熵:H(DA)=j=1mDjDi=1cp(xi)logp(xi)H(D|分裂屬性A)=\sum_{j=1}^m \frac{|D_j|}{|D|}*\sum^c_{i=1}p(x_i)logp(x_i)某屬性分裂後的分裂信息熵:SplitInfA=j=1mDjDlog2DjDSplitInf_A=-\sum_{j=1}^m\frac{|D_j|}{|D|}log_2\frac{|D_j|}{|D|}信息增益率:gain_ratio(A)=gain(A)SplitInfAgain\_ratio(分裂屬性A)=\frac{gain(分裂屬性A)}{SplitInf_A}
2、案例計算
SplitInf()=310log2310410log2410310log2310=1.5709SplitInf(日誌密度)=-\frac{3}{10}log⁡_2\frac{3}{10}-\frac{4}{10}log⁡_2\frac{4}{10}-\frac{3}{10}log⁡_2\frac{3}{10}=1.5709 SplitInf()=410log2410410log2410210log2210=1.5219SplitInf(好友密度)=-\frac{4}{10}log⁡_2\frac{4}{10}-\frac{4}{10}log⁡_2\frac{4}{10}-\frac{2}{10}log⁡_2\frac{2}{10}=1.5219 SplitInf(使)=510log2510510log2510=1SplitInf(是否使用真實頭像)=-\frac{5}{10}log⁡_2\frac{5}{10}-\frac{5}{10}log⁡_2\frac{5}{10}=1 Gain_Ratio()=gain()splitInf()=0.28131.5709=0.1791Gain\_Ratio(日誌密度)=\frac{gain(日誌密度)}{splitInf(日誌密度)}=\frac{0.2813}{1.5709}=0.1791 Gain_Ratio()=gain()splitInf()=0.55681.5219=0.3659Gain\_Ratio(好友密度)=\frac{gain(好友密度)}{splitInf(好友密度)}=\frac{0.5568}{1.5219}=0.3659 Gain_Ratio(使)=gain(使)splitInf(使)=0.03491=0.0349Gain\_Ratio(是否使用真實頭像)=\frac{gain(是否使用真實頭像)}{splitInf(是否使用真實頭像)}=\frac{0.0349}{1}=0.0349 Gain_Ratio()>Gain_Ratio()>Gain_Ratio(使)Gain\_Ratio(好友密度)>Gain\_Ratio(日誌密度)>Gain\_Ratio(是否使用真實頭像)因此,首先選擇好友密度作爲分裂屬性。
3、C4.5算法的優點
①解決了ID3算法偏向於多值屬性的缺陷(引入SplitInfSplitInf
②能夠處理缺失值
對於上例,若日誌密度存在缺失值如下:

日誌密度 好友密度 是否使用真實頭像 賬戶是否真實
s l yes yes
m l no yes
l m yes yes
m m yes yes
l m yes yes
? m no yes
s s no no
m s no no
? s no yes
s s yes no
日誌密度 分類:yes 分類:no 合計
s 1 2 3
m 2 1 3
l 2 0 2

Step1Step1'去除缺失值後此屬性中各水平的比例
對任意未知的日誌密度的值。取“s”的概率爲3/8;取“m”的概率爲3/8;取“l”的概率爲2/8。
Step2Step2'按比例填充
S1:日誌密度=s的數量——3+2*(3/8)
S2:日誌密度=m的數量——3+2*(3/8)
S3:日誌密度=l的數量——2+2*(2/8)
③能處理連續變量
C4.5算法處理連續屬性的原理是:對不同的可能劃分,分別計算所得的信息增益率,選擇信息增益率最大的那個劃分區間。
Step1Step 1' 將屬性取值排序,計算相鄰值的中點 viv_i
Step2Step 2'對其中每個取值viv_i作爲閾值 將數據集劃分爲兩個部分;
Step3Step 3' 計算每個分支的信息增益率;
Step4Step 4' 選擇最大信息增益的分支作爲閾值,將數據分裂爲離散型
PS.詳情可見《數據挖掘導論》P99-P100

(3)CART算法(Classification and Regression Tree)

1、分類樹

CART算法原理:使用二叉樹將預測空間劃分爲若干子集,隨着從根節點到葉節點的移動,每個節點選出最優的分支規則對應的劃分區域,目標變量在該節點上的條件分佈也隨之被確定。
分類準則——基尼係數
首先,介紹CART算法的計算公式:
總Gini:Gini(D)=1i=1cp(xi)2Gini(D)=1-\sum^c_{i=1}p(x_i)^2 某屬性分裂後的Gini:Gini(DA)=j=1mDjDGini(Dj)Gini(D|分裂屬性A)=\sum_{j=1}^m\frac{|D_j|}{|D|}*Gini(D_j) Gini增益:ΔGini(DA)=Gini(D)Gini(DA)\Delta Gini(D|分裂屬性A)=Gini(D)-Gini(D|分裂屬性A)
以是否使用真實頭像爲例:
在這裏插入圖片描述
Gini(使)=510(1(45)2(15)2)+510(1(35)2(25)2)Gini(是否使用真實頭像)=\frac{5}{10}* (1-(\frac{4}{5})^2-(\frac{1}{5})^2 )+\frac{5}{10}* (1-(\frac{3}{5})^2-(\frac{2}{5})^2 ) 樣本分佈均勻時Gini指標最大;分類越純Gini越小。選擇Gini係數增益最大的變量最爲分類屬性。

2、迴歸樹

分類準則:最小方差原理
①基於樹的方法:將特徵空間劃分成一系列長方形,然後對每個長方形擬合簡單的模型(常數)
在這裏插入圖片描述
擬合的模型爲:f^(x)=(m=1)5cmI{X1,X2Rm}\hat f(x)=\sum_{(m=1)}^5c_m I\{X_1,X_2∈R_m\} ②迴歸樹的分裂準則——最小方差法
對每個變量、根據不同的分割點計算二叉樹兩邊分裂的數值均值,計算其方差,方差最小的分裂點爲其最終分裂點,各個變量方差值對比,選擇最小方差的變量作爲分裂屬性。
迴歸樹的生成計算請戳☞[傳送門]☜

決策樹剪枝

{決策樹剪枝方法\begin{cases}預剪枝:在樹完全生長之前停止部分分裂\\後剪枝:在樹完全生長之後減去部分枝丫\end{cases}

預剪枝(Pre-Pruning)

①若節點中所有觀測屬於一類[沒必要再分],停止劃分
②若樹的深度達到預定閾值,停止劃分
③若該節點所含觀測值小於設定父節點應含觀測值的閾值,停止劃分
④若該節點子節點所含觀測數小於設定閾值,停止劃分
⑤若沒有屬性滿足設定的分裂準則的閾值[純度增加量不夠],停止劃分

後剪枝(Post-Pruning)

以CCP(Cost Complexity Pruning)爲例——

CCP剪枝:選擇節點表面誤差率增益值最小的非葉子節點,刪除該非葉子節點的左右子節點,若有多個非葉子節點的表面誤差率增益值相同小,則選擇非葉子節點中子節點數最多的非葉子節點進行剪枝。

CCP剪枝的步驟:
1)從原始的決策樹開始生成一個子樹序列(均非葉子節點){T0,T1,,Tn}\{T_0,T_1,…,T_n \},其中T(i+1)T_{(i+1)}TiT_i 產生,TnT_n 爲根節點
2)計算所有節點的誤差率增益值,選擇誤差率增益值最小的非葉子節點
3)對選中節點進行剪枝
4)重複
誤差率增益值的計算公式爲:α=R(t)R(T)N(T)1\alpha=\frac{R(t)-R(T)}{N(T)-1} ((非葉節點誤差-葉節點誤差)/(葉節點數-1))
其中,

  • α 衡量的是每個節點所能減少的分類誤差率。α越小,說明該節點所能減少的分類誤差率越小,繼續往下分類意義不大,可以剪枝,將該結點作爲葉結點。

  • R(t)-表示非葉子節點的誤差代價,R(t)=r(t)*p(t), r(t)爲該節點的錯誤率, p(t) 爲節點的數據量佔比

  • R(T)-表示子樹葉子節點的誤差代價,R(T)=(i=1)mri(t)pi(t)R(T)=\sum_{(i=1)}^mr_i (t) p_i (t), ri(t)r_i (t)
    爲子節點i的錯誤率, pi(t)p_i (t) 表示節點i的數據量佔比

  • N(T)-表示子樹的葉結點個數
    eg.在這裏插入圖片描述
    總樣本量:55+25=80
    ① 對t4節點,
    錯誤佔比:446+4\frac{4}{46+4}
    R(t)=r(t)p(t)R(t)=r(t)*p(t),t4錯誤率佔所有觀測的佔比情況:R(t4)=4505080=120R(t4)=\frac{4}{50}*\frac{50}{80}=\frac{1}{20}
    R(T)=(i=1)mri(t)pi(t)R(T)=\sum^m_{(i=1)} r_i (t) p_i (t)
    t8和t9葉節點錯誤率:R(T4)=1454580+25580=380R(T4)=\frac{1}{45}*\frac{45}{80}+\frac{2}{5}*\frac{5}{80}=\frac{3}{80} (N(t)=2,兩個葉節點)
    CCP誤差爲:α=R(t)R(T)N(T)1=12038021=0.0125α=\frac{R(t)-R(T)}{N(T)-1}=\frac{\frac{1}{20}-\frac{3}{80}}{2-1}=0.0125
    ② 對於非葉子節點t3的子樹
    節點錯誤率:515+5\frac{5}{15+5}
    t3節點錯誤佔比:R(t3)=5202080=116R(t3)=\frac{5}{20}*\frac{20}{80}=\frac{1}{16}
    t6,t7的錯誤率:R(T3)=15580+1151580=140R(T3)=\frac{1}{5}*\frac{5}{80}+\frac{1}{15}*\frac{15}{80}=\frac{1}{40} (N(t)=2,兩個葉節點)
    CCP誤差爲: α=R(t)R(T)N(T)1=11614021=0.0375α=\frac{R(t)-R(T)}{N(T)-1}=\frac{\frac{1}{16}-\frac{1}{40}}{2-1}=0.0375
    ③ 對於非葉子節點t2的子樹,
    節點誤差佔比:R(t)=r(t)p(t)=10606080=18R(t)=r(t)*p(t)=\frac{10}{60}*\frac{60}{80}=\frac{1}{8}
    對應葉子節點誤差率:R(T)=(i=1)mri(t)pi(t)=1454580+25580+06680+04480=380R(T)=\sum_{(i=1)}^m r_i (t)*p_i (t)=\frac{1}{45}*\frac{45}{80}+\frac{2}{5}*\frac{5}{80}+\frac{0}{6}*\frac{6}{80}+\frac{0}{4}*\frac{4}{80}=\frac{3}{80},N(T)=4
    CCP誤差爲: α=R(t)R(T)(N(T)1=1838041=0.0292α=\frac{R(t)-R(T)}{(N(T)-1}=\frac{\frac{1}{8}-\frac{3}{80}}{4-1}=0.0292 關於上面決策樹的所有節點的α值計算結果爲:(自下而上剪枝)
    T0α(t4)=0.0125α(t5)=0.05α(t2)=0.0292α(t3)=0.0375T_0: α(t4)=0.0125 α(t5)=0.05 α(t2)=0.0292 α(t3)=0.0375(減去t4枝葉變爲葉節點)
    T1α(t5)=0.05α(t2)=0.0375α(t3)=0.0375T_1:α(t5)=0.05 α(t2)=0.0375 α(t3)=0.0375(t2與t3的α 值相同,但是裁剪掉前者可以得到更小的決策樹,減去t2變爲葉節點)
    以上就是CCP代價複雜度剪枝的全過程。

決策樹的Python實現

例子依然採用上述手算例:

日誌密度 好友密度 是否使用真實頭像 賬戶是否真實
s l yes yes
m l no yes
l m yes yes
m m yes yes
l m yes yes
l m no yes
s s no no
m s no no
m s no yes
s s yes no

1、導入需要的包

import pandas as pd
import numpy as np
from sklearn import tree
from sklearn.preprocessing import LabelEncoder

2、讀入數據

data = pd.read_csv('data/example.csv',engine='python')
data

3、數據處理

# 多值型分類變量轉化
x1 = pd.get_dummies(pd.DataFrame(data['日誌密度'].values.tolist()),prefix='日誌密度',prefix_sep='_',drop_first=True).\
groupby(axis=1,level=0).max()

x2 = pd.get_dummies(pd.DataFrame(data['好友密度'].values.tolist()),prefix='好友密度',prefix_sep='_',drop_first=True).\
groupby(axis=1,level=0).max()

del data['日誌密度']
del data['好友密度']

data = pd.concat([data,x1,x2],axis=1)
data

# 二值型變量轉化
data['是否使用真實頭像'] =  LabelEncoder().fit_transform(np.array(data['是否使用真實頭像']))
data['賬戶是否真實'] =  LabelEncoder().fit_transform(np.array(data['賬戶是否真實']))

4、數據轉化

y = data['賬戶是否真實']
x = data.drop(['賬戶是否真實'],axis=1)

5、建模

tree_model = tree.DecisionTreeClassifier(criterion='gini')
tree_model.fit(x,y)

6、畫決策樹

import graphviz

dot_data = tree.export_graphviz(tree_model,
                               out_file=None,
                               feature_names= ['是否使用真實頭像', '日誌密度_m', '日誌密度_s', '好友密度_m', '好友密度_s'],
                               class_names= ['no','yes'],
                               filled=True,
                               rounded=True,
                               special_characters=True)

graph = graphviz.Source(dot_data)
graph

得到如下圖:
在這裏插入圖片描述
可見,和上述手算例得到相同結果!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章