日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

C4.5决策树生成算法完整版(Python),连续属性的离散化, 缺失样本的添加权重处理, 算法缺陷的修正, 代码等

發布時間:2024/7/19 python 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C4.5决策树生成算法完整版(Python),连续属性的离散化, 缺失样本的添加权重处理, 算法缺陷的修正, 代码等 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

C4.5決策樹生成算法完整版(Python)

轉載請注明出處:?? Sylvan Ding


ID3算法實驗

決策樹從一組無次序、無規則的事例中推理出決策樹表示的分類規則,采用自頂向下的遞歸方式,在決策樹的內部節點進行屬性值的比較并根據不同的屬性值判斷從該結點向下的分支,在決策樹葉結點得到結論。

實驗目的

  • 理解ID3算法原理
  • 理解C4.5算法原理
  • 編程實現C4.5算法
  • 決策樹生成算法

    決策樹的生成過程是使用滿足劃分準則的特征不斷地將數據集劃分為純度更高、不確定性更小的子集的過程。

    Generate_decision_tree 輸入: 訓練樣本samples,由離散值屬性表示;候選屬性的集合attribute_list。 輸出: 一顆決策樹 創建結點N IF samples都在同一個類C THEN返回N作為葉結點,以類C標記; IF attribute_list為空 THEN返回N作為葉結點,標記為samples中最普通的類; 選擇attribute_list中具有最高信息增益(或增益率)的屬性test_attribute; 標記結點N為test_attribute; FOR each test_attribute中的已知值ai //劃分samples由結點N長出一個條件為test_attribute=ai的分支; 設si是samples中test_attribute=ai的樣本集合 //一個劃分 IF si為空 THEN加上一個樹葉,標記為samples中最普通的類; ELSE 加上一個由Generate_decision_tree(si, attribute_list - test_attribute)返回的結點;

    ID3算法原理

    • 決策樹中每一個非葉結點對應著一個非類別屬性,樹枝代表這個屬性的值。一個葉結點代表從樹根到葉結點之間的路徑對應的記錄所屬的類別屬性值。
    • 采用信息增益來選擇最佳劃分屬性。

    信息增益計算

    ID3總是選擇具有最高信息增益(最大熵)的屬性作為當前結點的測試屬性(test_attribute)。

    設S是s個數據樣本的集合,假定類標號屬性有m個不同值,定義m個不同類Ci(i=1,2,…,m)C_i(i=1,2,\dots,m)Ci?(i=1,2,,m)。設sis_isi?是類CiC_iCi?中的樣本數。對一個給定的樣本分類所需的期望信息為:

    I(s1,s2,…,sm)=?∑i=1mpilog?(pi)I(s_1,s2,\dots,s_m) = -\sum_{i=1}^{m}p_i\log(p_i)I(s1?,s2,,sm?)=?i=1m?pi?log(pi?)

    其中,pip_ipi?是任意樣本屬于CiC_iCi?的概率,pi=sisp_i=\frac{s_i}{s}pi?=ssi??

    設屬性A具有v個不同值,則可用屬性A將S劃分為v個子集,設sijs_{ij}sij?是子集SjS_jSj?中類CiC_iCi?的樣本數,則根據A劃分子集的熵為:

    E(A)=?∑j=1v∑i=1msijsI(s1j,s2j,…,smj)E(A) = -\sum_{j=1}^{v}\frac{\sum_{i=1}^{m}s_{ij}}{s}I(s_{1j}, s_{2j}, \dots, s_{mj})E(A)=?j=1v?si=1m?sij??I(s1j?,s2j?,,smj?)

    由期望信息和熵值可以得到對應的信息增益值:

    Gain(A)=I(s1j,s2j,…,smj)?E(A)Gain(A)=I(s_{1j}, s_{2j}, \dots, s_{mj})-E(A)Gain(A)=I(s1j?,s2j?,,smj?)?E(A)

    ID3算法分析

    優點

    ID3算法避免了搜索不包含目標函數的不完整假設空間的主要風險,因為有限個離散值函數可以表示某個決策樹。

    缺點

    無法回溯爬山搜索中常見的風險,如收斂到局部最優,而不是全局最優。ID3算法只能處理離散值的屬性。

    當特征的取值較多時,根據此特征劃分更容易得到純度高的子集,因此劃分之后的熵更低,由于劃分前的熵是一定的,所以信息增益更大,ID3偏袒較多值的屬性。

    C4.5算法

    • 用信息增益率來代替信息增益
    • 合并具有連續屬性的值
    • 處理缺少屬性值的訓練樣本

    信息增益率

    GainRatio(A)=Gain(A)SplitI(A)GainRatio(A)=\frac{Gain(A)}{SplitI(A)}GainRatio(A)=SplitI(A)Gain(A)?

    其中,

    SplitI(A)=?∑j=1vpjlog?(pj)SplitI(A)=-\sum_{j=1}^{v}p_j\log(p_j)SplitI(A)=?j=1v?pj?log(pj?)

    連續屬性的離散化

    • 根據屬性的值,對數據集排序;
    • 用不同的閾值將數據集動態劃分;
    • 取兩個實際值中點作為一個閾值;
    • 取兩個劃分,所有樣本都在這兩個劃分中;
    • 得到所有可能的閾值和增益率;

    對連續屬性A進行排序,按閾值將A劃分為兩部分,一部分落入vjv_jvj?對范圍內,而另一部分則大于vjv_{j}vj?,選擇增益率最大的劃分所對應的閾值為劃分閾值進行屬性離散化。注意,當前劃分屬性為連續屬性,則該屬性還可以作為其后代的劃分屬性。新增labelProperties表示屬性為連續還是離散。

    缺失值處理

    選取最優劃分屬性

    有缺失值屬性的信息增益為該屬性(設該屬性為A)下的無缺失值樣本占比×\times×無缺失值樣本子集的信息增益。

    Gain(A)=p×Gain(A~)Gain(A) = p\times Gain(\widetilde{A})Gain(A)=p×Gain(A)

    其中,p為A屬性下無缺失值樣本的占比,即p=∑x∈D~wx∑x∈Dwxp=\frac{\sum_{x\in \widetilde{D}}w_x}{\sum_{x\in D}w_x}p=xD?wx?xD?wx??Gain(A~)Gain(\widetilde{A})Gain(A)為A屬性下無缺失值樣本的信息增益,wxw_xwx?是樣本x的權重,D是所有樣本,D~\widetilde{D}D是無缺失樣本。

    缺失值樣本的劃分

    增加樣本權重概念。樣本權重的初始值為1,對于無缺失值樣本,將其劃分到子結點時,權重保持不變。而對于有缺失值樣本,在劃分時按無缺失值樣本在每個分支中所占比重(即對于分支中無缺失值樣本數/該屬性下無缺失值樣本總數)劃分到分支中。此時,ID3中所得公式需改寫:

    Gain(A~)=I(S)?E(A)Gain(\widetilde{A})=I(S)-E(A)Gain(A)=I(S)?E(A)

    I(S)=?∑i=1mpi~log?(pi~)I(S)= -\sum_{i=1}^{m}\widetilde{p_i}\log(\widetilde{p_i})I(S)=?i=1m?pi??log(pi??)

    E(A)=?∑j=1vrj~I(Sj)E(A)=-\sum_{j=1}^v \widetilde{r_j}I(S^j)E(A)=?j=1v?rj??I(Sj)

    Sj=(s1j,s2j,…,smj)S^j=(s_{1j},s_{2j},\dots,s_{mj})Sj=(s1j?,s2j?,,smj?)

    S=(s1,s2,…,sm)S=(s_{1},s_{2},\dots,s_{m})S=(s1?,s2?,,sm?)

    pi~=∑x∈Di~wx∑x∈D~wx\widetilde{p_i}=\frac{\sum_{x\in \widetilde{D_i}}w_x}{\sum_{x\in \widetilde{D}}w_x}pi??=xD?wx?xDi???wx??

    rj~=∑x∈Dj~wx∑x∈D~wx\widetilde{r_j}=\frac{\sum_{x\in \widetilde{D^j}}w_x}{\sum_{x\in \widetilde{D}}w_x}rj??=xD?wx?xDj?wx??

    pi~\widetilde{p_i}pi??是無缺失值樣本中第i類所占比例,樣本個數按權重計算;rj~\widetilde{r_j}rj??是無缺失值樣本中屬性A上取值為aja_jaj?的樣本所占比例,樣本個數按權重計算。i是樣本索引,j是屬性索引。

    *缺失測試樣本的分類

    C4.5算法的決策樹構造完成后,需要對含缺失值的測試樣本進行分類。在為測試樣本某屬性下的未知值選擇分支時,考慮該缺失屬性在該分支下的每個葉子結點中屬于不同分類的概率,最大概率對于的分類即為所屬分類。概率的計算使用該分支下每個葉子結點中不同分類的權值的加權平均。

    本實驗僅涉及決策樹生成算法,故不考慮測試集的分類和決策樹剪枝算法。

    *C4.5算法的缺陷和修正

    當離散屬性和連續屬性并存時,C4.5算法傾向于選擇連續特征作為最佳樹分裂點。因此要對最佳分裂點的信息增益進行修正:

    Gain(A)=Gain(A)?log?(N?1)∣D∣Gain(A) = Gain(A)-\frac{\log{(N-1)}}{\left| D\right|}Gain(A)=Gain(A)?Dlog(N?1)?

    其中,N為連續特征可能的分裂點個數,D是樣本數目。

    此外,C4.5算法的信息增益率偏向取值較少的特征。因此,并不直接選擇信息增益率最大的特征,而是在候選特征中找出信息增益高于平均水平的特征,然后在這些特征中再選擇信息增益率最高的特征作為最佳劃分特征。

    本實驗編寫的決策樹生成算法,不考慮上述兩種問題的修正。

    代碼

    import copy import operator from math import log from numpy import infNAN = 'Nan' # 缺失值定義def calcShannonEnt(dataSet: list, labelIndex: int):"""計算對應屬性索引下樣本的香農熵:param dataSet: 樣本:param labelIndex: 屬性索引:return: shannonEnt 香農熵"""numEntries = 0 # 樣本數(按權重計算)labelCounts = {}# 遍歷樣本,計算每類的權重for featVec in dataSet:# 樣本的屬性不為空if featVec[labelIndex] != NAN:weight = featVec[-2]numEntries += weightcurrentLabel = featVec[-1] # 當前樣本的類別# 如果樣本類別不在labelCountsif currentLabel not in labelCounts.keys():# 添加該類別,令該類別權重為0labelCounts[currentLabel] = .0# 添加該類別的權重labelCounts[currentLabel] += weightshannonEnt = .0for key in labelCounts: # 計算信息熵prob = labelCounts[key] / numEntriesshannonEnt -= prob * log(prob, 2)return shannonEntdef splitDataSet(dataSet: list, axis: int, value, AttrType='N'):"""劃分數據集:param dataSet: 數據集:param axis: 按第幾個特征劃分:param value: 劃分特征的值:param AttrType: N-離散屬性; L-小于等于value值; R-大于value值:return: 對應axis為value(連續情況下則為大于或小于value)的數據集dataSet的子集"""subDataSet = []# N-離散屬性if AttrType == 'N':for featVec in dataSet:if featVec[axis] == value:reducedFeatVec = featVec[:axis]reducedFeatVec.extend(featVec[axis + 1:])subDataSet.append(reducedFeatVec)# L-小于等于value值elif AttrType == 'L':for featVec in dataSet:# 樣本axis對應屬性非空if featVec[axis] != NAN:if featVec[axis] <= value:# 無需減少該特征subDataSet.append(featVec)# R-大于value值elif AttrType == 'R':for featVec in dataSet:if featVec[axis] != NAN:if featVec[axis] > value:# 無需減少該特征subDataSet.append(featVec)else:exit(0)return subDataSetdef calcTotalWeight(dataSet: list, labelIndex: int, isContainNull: bool):"""計算樣本對某個特征值的總樣本數(按權重計算):param dataSet: 數據集:param labelIndex: 屬性索引:param isContainNull: 是否包含空值:return: 樣本的總權重"""totalWeight = .0# 遍歷樣本for featVec in dataSet:# 樣本權重weight = featVec[-2]# 不包含空值并且該屬性非空if isContainNull is False and featVec[labelIndex] != NAN:# 非空樣本樹,按權重計算totalWeight += weight# 包含空值if isContainNull is True:# 總樣本數totalWeight += weightreturn totalWeightdef splitDataSetWithNull(dataSet: list, axis: int, value, AttrType='N'):"""劃分含有缺失值的數據集:param dataSet: 數據集:param axis: 按第幾個特征劃分:param value: 劃分特征的值:param AttrType: N-離散屬性; L-小于等于value值; R-大于value值:return: 按value劃分的數據集dataSet的子集"""# 屬性值未缺失樣本子集subDataSet = []# 屬性值缺失樣本子集nullDataSet = []# 計算非空樣本總權重totalWeightV = calcTotalWeight(dataSet, axis, False)# N-離散屬性if AttrType == 'N':for featVec in dataSet:if featVec[axis] == value:reducedFeatVec = featVec[:axis]reducedFeatVec.extend(featVec[axis + 1:])subDataSet.append(reducedFeatVec)# 樣本該屬性值缺失elif featVec[axis] == NAN:reducedNullVec = featVec[:axis]reducedNullVec.extend(featVec[axis + 1:])nullDataSet.append(reducedNullVec)# L-小于等于value值elif AttrType == 'L':for featVec in dataSet:# 樣本該屬性值未缺失if featVec[axis] != NAN:if value is None or featVec[axis] < value:subDataSet.append(featVec)# 樣本該屬性值缺失elif featVec[axis] == NAN:nullDataSet.append(featVec)# R-大于value值elif AttrType == 'R':for featVec in dataSet:# 樣本該屬性值未缺失if featVec[axis] != NAN:if featVec[axis] > value:subDataSet.append(featVec)# 樣本該屬性值缺失elif featVec[axis] == NAN:nullDataSet.append(featVec)# 計算此分支中非空樣本的總權重totalWeightSub = calcTotalWeight(subDataSet, -1, True)# 缺失值樣本按權值比例劃分到分支中for nullVec in nullDataSet:nullVec[-2] = nullVec[-2] * totalWeightSub / totalWeightVsubDataSet.append(nullVec)return subDataSetdef calcGainRatio(dataSet: list, labelIndex: int, labelType: bool):"""計算信息增益率,返回信息增益率和連續屬性的劃分點:param dataSet: 數據集:param labelIndex: 屬性索引:param labelType: 屬性類型,0為離散,1為連續:return: 信息增益率和連續屬性的劃分點"""# 計算根節點的信息熵baseE = calcShannonEnt(dataSet, labelIndex)# 對應labelIndex的特征值向量featVec = [row[labelIndex] for row in dataSet]# featVec值的種類uniqueVals = set(featVec)newE = .0 # 新信息熵bestPivotValue = None # 最佳劃分屬性IV = .0 # 該變量取自西瓜書# 總樣本權重totalWeight = calcTotalWeight(dataSet, labelIndex, True)# 非空樣本權重totalWeightV = calcTotalWeight(dataSet, labelIndex, False)# 對離散的特征if labelType == 0:# 按屬性值劃分數據集,計算各子集的信息熵for value in uniqueVals:# 劃分數據集subDataSet = splitDataSet(dataSet, labelIndex, value)# 計算子集總權重totalWeightSub = calcTotalWeight(subDataSet, labelIndex, True)# 過濾空屬性if value != NAN:prob = totalWeightSub / totalWeightVnewE += prob * calcShannonEnt(subDataSet, labelIndex)prob1 = totalWeightSub / totalWeightIV -= prob1 * log(prob1, 2)# 對連續的特征else:uniqueValsList = list(uniqueVals)# 過濾空屬性if NAN in uniqueValsList:uniqueValsList.remove(NAN)# 計算空值樣本的總權重,用于計算IVdataSetNull = splitDataSet(dataSet, labelIndex, NAN)totalWeightN = calcTotalWeight(dataSetNull, labelIndex, True)probNull = totalWeightN / totalWeightif probNull > 0:IV += -1 * probNull * log(probNull, 2)# 屬性值排序sortedUniqueVals = sorted(uniqueValsList)minEntropy = inf # 定義最小熵# 如果UniqueVals只有一個值,則說明只有左子集,沒有右子集if len(sortedUniqueVals) == 1:totalWeightL = calcTotalWeight(dataSet, labelIndex, True)probL = totalWeightL / totalWeightVminEntropy = probL * calcShannonEnt(dataSet, labelIndex)IV = -1 * probL * log(probL, 2)# 如果UniqueVals只有多個值,則計算劃分點else:for j in range(len(sortedUniqueVals) - 1):pivotValue = (sortedUniqueVals[j] + sortedUniqueVals[j + 1]) / 2# 對每個劃分點,劃分得左右兩子集dataSetL = splitDataSet(dataSet, labelIndex, pivotValue, 'L')dataSetR = splitDataSet(dataSet, labelIndex, pivotValue, 'R')# 對每個劃分點,計算左右兩側總權重totalWeightL = calcTotalWeight(dataSetL, labelIndex, True)totalWeightR = calcTotalWeight(dataSetR, labelIndex, True)probL = totalWeightL / totalWeightVprobR = totalWeightR / totalWeightVEnt = probL * calcShannonEnt(dataSetL, labelIndex) + probR * calcShannonEnt(dataSetR, labelIndex)# 取最小的信息熵if Ent < minEntropy:minEntropy = EntbestPivotValue = pivotValueprobL1 = totalWeightL / totalWeightprobR1 = totalWeightR / totalWeightIV += -1 * (probL1 * log(probL1, 2) + probR1 * log(probR1, 2))newE = minEntropygain = totalWeightV / totalWeight * (baseE - newE)# 避免IV為0(屬性只有一個值的情況下)if IV == 0.0:IV = 0.0000000001gainRatio = gain / IVreturn gainRatio, bestPivotValuedef chooseBestFeatureToSplit(dataSet: list, labelProps: list):"""選擇最佳數據集劃分方式:param dataSet: 數據集:param labelProps: 屬性類型,0離散,1連續:return: 最佳劃分屬性的索引和連續屬性的最佳劃分值"""numFeatures = len(labelProps) # 屬性數bestGainRatio = -inf # 最大信息增益bestFeature = -1 # 最優劃分屬性索引bestPivotValue = None # 連續屬性的最佳劃分值for featureI in range(numFeatures): # 對每個特征循環gainRatio, bestPivotValuei = calcGainRatio(dataSet, featureI, labelProps[featureI])# 取信息益率最大的特征if gainRatio > bestGainRatio:bestGainRatio = gainRatiobestFeature = featureIbestPivotValue = bestPivotValueireturn bestFeature, bestPivotValuedef majorityCnt(classList: list, weightList: list):"""返回出現次數最多的類別(按權重計):param classList: 類別:param weightList: 權重:return: 出現次數最多的類別"""classCount = {}# 計算classCountfor cls, wei in zip(classList, weightList):if cls not in classCount.keys():classCount[cls] = .0classCount[cls] += wei# 排序sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)# 僅剩一個類別if len(sortedClassCount) == 1:return sortedClassCount[0][0], sortedClassCount[0][1]# 剩余多個類別,返回出現次數最多的類別return sortedClassCount[0][0], sortedClassCount[0][1]def isSame(dataSet: list):"""比較樣本特征是否相同:param dataSet: 數據集:return: 相同True,否則False"""for j in range(len(dataSet[0])-2):for i in range(1, len(dataSet)):if not dataSet[i][j] == dataSet[0][j]:return Falsereturn Truedef createTree(dataSet: list, labels: list, labelProps: list):"""創建決策樹(Decision Tree):param dataSet: 數據集:param labels: 屬性集:param labelProps: 屬性類型,0離散,1連續:return: 決策樹"""classList = [sample[-1] for sample in dataSet] # 類別向量weightList = [sample[-2] for sample in dataSet] # 權重向量# 如果只剩一個類別,返回并退出if classList.count(classList[0]) == len(classList):totalWeight = calcTotalWeight(dataSet, 0, True)return classList[0], totalWeight# 如果所有特征都遍歷完了,返回出現次數最多的類別,并退出if len(dataSet[0]) == 1:return majorityCnt(classList, weightList)# 如果剩余樣本特征相同,返回出現次數最多的類別,并退出if isSame(copy.copy(dataSet)):return majorityCnt(classList, weightList)# 計算最優分類特征的索引,若為連續屬性,則還返回連續屬性的最優劃分點bestFeat, bestPivotValue = chooseBestFeatureToSplit(dataSet, labelProps)# 對離散的特征if labelProps[bestFeat] == 0:bestFeatLabel = labels[bestFeat]myTree = {bestFeatLabel: {}}labelsNew = copy.copy(labels)labelPropertyNew = copy.copy(labelProps)# 已經選擇的離散特征不再參與分類del (labelsNew[bestFeat])del (labelPropertyNew[bestFeat])featValues = [sample[bestFeat] for sample in dataSet]# 最佳花劃分屬性包含的所有值uniqueValue = set(featValues)# 刪去缺失值uniqueValue.discard(NAN)# 遍歷每個屬性值,遞歸構建樹for value in uniqueValue:subLabels = labelsNew[:]subLabelProperty = labelPropertyNew[:]myTree[bestFeatLabel][value] = createTree(splitDataSetWithNull(dataSet, bestFeat, value),subLabels, subLabelProperty)# 對連續特征,不刪除該特征,分別構建左子樹和右子樹else:bestFeatLabel = labels[bestFeat] + '<' + str(bestPivotValue)myTree = {bestFeatLabel: {}}subLabels = labels[:]subLabelProperty = labelProps[:]# 構建左子樹valueLeft = 'Y'myTree[bestFeatLabel][valueLeft] = createTree(splitDataSetWithNull(dataSet, bestFeat, bestPivotValue, 'L'),subLabels, subLabelProperty)# 構建右子樹valueRight = 'N'myTree[bestFeatLabel][valueRight] = createTree(splitDataSetWithNull(dataSet, bestFeat, bestPivotValue, 'R'),subLabels, subLabelProperty)return myTreeif __name__ == '__main__':# 讀取數據文件fr = open(r'data.csv')data = [row.strip().split(',') for row in fr.readlines()]labels = data[0][0:-1] # labels:屬性dataset = data[1:] # dataset:數據集(初始樣本)labelProperties = [0, 1, 0] # labelProperties:屬性標識,0為離散,1為連續# 樣本權重初始化for row in dataset:row.insert(-1, 1.0)# 按labelProperties連續化離散屬性for row in dataset:for i, lp in enumerate(labelProperties):# 若標識為連續屬性,則轉化為float型if lp:row[i] = float(row[i])# C4.5算法生成決策樹trees = createTree(copy.copy(dataset), copy.copy(labels), copy.copy(labelProperties))print(trees)

    Python3.6

    結果驗證

    在data.csv數據集上運行上述代碼,得到結果如下:

    {'天氣': {'多云': ('玩', 3.230769230769231), '晴': {'濕度<77.5': {'Y': ('玩', 2.0), 'N': {'有雨?': {'有': ('不玩', 1.0), '無': ('不玩', 2.0)}}}}, '雨': {'有雨?': {'有': {'濕度<85.0': {'Y': ('不玩', 2.0), 'N': ('玩', 0.38461538461538464)}}, '無': ('玩', 3.0)}}}} // (結果, 權重)

    附錄(data.csv)

    天氣濕度有雨?去玩?
    70
    90不玩
    85不玩
    95不玩
    70
    Nan90
    多云78
    多云65
    多云75
    80不玩
    70不玩
    80
    80
    96

    參考

  • 數據挖掘原理與算法(第3版)
  • 《機器學習》周志華
  • 決策樹–信息增益,信息增益比,Geni指數的理解
  • 機器學習筆記(5)——C4.5決策樹中的連續值處理和Python實現
  • 機器學習筆記(7)——C4.5決策樹中的缺失值處理
  • 總結

    以上是生活随笔為你收集整理的C4.5决策树生成算法完整版(Python),连续属性的离散化, 缺失样本的添加权重处理, 算法缺陷的修正, 代码等的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。