1. 程式人生 > 其它 >20192320 楊坤《python》實驗四報告

20192320 楊坤《python》實驗四報告

20192320 《Python程式設計》實驗四報告

課程:《Python程式設計》
班級: 1923班
姓名: 楊坤
學號: 20192320
實驗教師:王志強
實驗日期:2022年5月30日
必修/選修: 公選課

1.實驗內容

Python綜合應用:爬蟲、資料處理、視覺化、機器學習、神經網路、遊戲、網路安全等。
課代表和各小組負責人收集作業(原始碼、視訊、綜合實踐報告)
注:在華為ECS伺服器(OpenOuler系統)和物理機(Windows/Linux系統)上使用VIM、PDB、IDLE、Pycharm等工具程式設計實現。

批閱:注意本次實驗不算做實驗總分,前三個實驗每個實驗10分,累計30分。本次實踐算入綜合實踐,打分為25分。
評分標準:
(1)程式能執行,功能豐富。(需求提交原始碼,並建議錄製程式執行的視訊)10分
(2)綜合實踐報告,要體現實驗分析、設計、實現過程、結果等資訊,格式規範,邏輯清晰,結構合理。10分。
(3)在實踐報告中,需要對全課進行總結,並寫課程感想體會、意見和建議等。5分
(4)如果沒有使用華為雲服務(ECS或者MindSpore均可),本次實踐扣10分。
注意:每個人的實驗不能重複,課代表

2資訊熵

隨機變數\(P_k\)的熵定義為:

\[Ent(D)=-\sum_{k=1}^{\lvert{\gamma}\rvert}p_k\log{(p_k)}\]

對於具體的、隨機變數y生成的資料集\(D=\left\{y_1,.....,y_{N}\right\}\),在實際操作中通常利用經驗熵來估計資訊熵。

\[ \ H(D)=-\sum_{k=1}^K\frac{|C_k|}{|D|}\log\frac{|C_k|}{|D|} \]

假設隨機變數\(y\)的取值空間為\(\left\{c_1,......,c_k\right\}\),\(p_k\)表示\(y\)\(c_k\)

的概率:\(p_k=p(y=c_k)\)\(\left|C_k\right|\),代表變數\(y\)中類別為\(c_k\)的樣本的個數,\(\left|D\right|\),代表D的總樣本數。若對數的底數是2,資訊熵的單位是位元(bit),若對數的底數是\(e\),資訊熵的單位是納特(nat)。
當:
\(p_1=p_2=...=p_k=1/K\)時,\(H(y)\)達到最大值\(-log\frac{1}{K}\),此時意味著隨機變數\(y\)取每一個變數的概率是相等的,即\(y\)沒有規律可循。
我們的目的是讓\(y\)的不去定性減小,讓\(y\)變得有規律方便我們預測。

3.條件熵

條件熵\(H(y|A)\)

的概念來定義資訊的增益:

  • 條件熵就是根據A的不同取值\(\left\{a_1,...,a_m\right\}\)\(y\)進行限制後,先對這些被限制的\(y\)分別計算資訊熵,再把這些資訊熵根據特徵值本身的概率加權求和,從而得到總的條件熵。
    所以條件熵\(H(y|A)\)越小,意味著\(y\)被A限制後的總的不確定性越小,從而意味著A更能幫助我們做出決策。
\[H(y|A)=\sum_{j=1}^mp(A=a_j)H(y|A=a_j) \]

其中

\[H(y|A=a_j)=-\sum_{k=1}^{K}p(y=c_k|A=a_j)\log{p(y=c_k|A=a_j)} \]

即為:

\[ H(y|A)=H(y|D)=\sum_{i=1}^n\frac{|D_i|}{|D|}H(D_i)=-\sum_{i=1}^n\frac{|D_i|}{|D|}\sum_{k=1}^K\frac{|D_{ik}|}{|D_i|}\log\frac{|D_{ik}|}{|D_i|} \]

特徵A對訓練資料集D的資訊增益\(g(D,A)\),定義為集合D的經驗熵\(H(D)\)與特徵A給定條件下D的條件熵\(H(D|A)\)之差,即為:

\[g(D,A)=H(D)-H(D|A) \]

程式碼:

def shannoEnt(dataSet):
    labelCount = {}
    numOfData = len(dataSet)
    for data in dataSet:
        classify = data[-1]
        if classify not in labelCount.keys():
            labelCount[classify] = 1
        else:
            labelCount[classify] += 1
    H = 0.0
    for value in labelCount.values():
        pi = value / numOfData
        H -= pi * log(pi, 2)
    return H


def chooseBestFeatureSplit(dataSet):
    HD = shannoEnt(dataSet)
    bestGain = 0.0
    bestFeature = -1
    for i in range(len(dataSet[0]) - 1):
        feat = [data[i] for data in dataSet]
        prob = 0.0
        for value in set(feat):
            subData = splitData(dataSet, i, value)
            prob += (len(subData) / len(dataSet)) * shannoEnt(subData)
        Gain = HD - prob
        if Gain > bestGain:
            bestGain = Gain
            bestFeature = i
    # print('最優特徵是:',label[bestFeature])
    return bestFeature

4.ID3演算法

  1. 從根結點(root node)開始,對結點計算所有可能的特徵的資訊增益,選擇資訊增益最大的特徵作為結點的特徵,由該特徵的不同取值建立子結點
  2. 再對子結點遞迴地呼叫以上方法,構建決策樹
  3. 直到所有特徵的資訊增益均很小或沒有特徵可以選擇為止
  4. 最後得到一個決策樹

\(D\)中所有例項屬於同一類\(C_k\),則\(T\)為單結點樹,返回\(T\)
\(A=\emptyset\),則\(T\)為單結點樹,並將\(D\)中例項數最大的類\(C_k\)作為該結點的類標記,返回\(T\)
否則,計算\(A\)中各特徵對\(D\)的資訊增益,選擇資訊增益最大的特徵\(A_g\)

  • 如果\(A_g\)的資訊增益小於閾值\(\epsilon\),則\(T\)為單結點樹,並將\(D\)中例項數最大的類\(C_k\)作為該結點的類標記,返回\(T\)
  • 否則,依\(A_g=a_i\)\(D\)分割為若干非空子集\(D_i\),遞迴地構建子樹(訓練集:\(D_i\),特徵集:\(A-\{A_g\}\)

程式碼:

def createTree(dataSet, labels):
    classList = [example[-1] for example in dataSet]
    if len(set(classList)) == 1:
        return classList[0]
    if len(dataSet[0]) == 0:
        return majorityCnt(classList)
    bestFeat = chooseBestFeatureSplit(dataSet)
    bestFeatLabel = labels[bestFeat]

    # 建立樹:
    myTree = {bestFeatLabel: {}}

    del (labels[bestFeat])

    featValues = [example[bestFeat] for example in dataSet]



    for value in set(featValues):
        subLabels = labels[:]
        subDataSet = splitData(dataSet, bestFeat, value)

        myTree[bestFeatLabel][value] = createTree(subDataSet, subLabels)
    for key, value in myTree.items():
        print(key, value)
    return myTree

總的程式碼:

# coding:utf-8
'''
**************************************************
@File   :myPycharm -> ex_03
@IDE    :PyCharm
@Author :20192320楊坤
@Date   :2022/5/22 15:28
**************************************************
'''
from math import log


def loadDataSet():
    dataSet = [['青年', '否', '否', '一般', '否'],
               ['青年', '否', '否', '好', '否'],
               ['青年', '是', '否', '好', '是'],
               ['青年', '是', '是', '一般', '是'],
               ['青年', '否', '否', '一般', '否'],
               ['中年', '否', '否', '一般', '否'],
               ['中年', '否', '否', '好', '否'],
               ['中年', '是', '是', '好', '是'],
               ['中年', '否', '是', '非常好', '是'],
               ['中年', '否', '是', '非常好', '是'],
               ['老年', '否', '是', '非常好', '是'],
               ['老年', '否', '是', '好', '是'],
               ['老年', '是', '否', '好', '是'],
               ['老年', '是', '否', '非常好', '是'],
               ['老年', '否', '否', '一般', '否']]
    label = ['年齡', '有工作', '有自己的房子', '信貸情況']
    return dataSet, label


def shannoEnt(dataSet):
    labelCount = {}
    numOfData = len(dataSet)
    for data in dataSet:
        classify = data[-1]
        if classify not in labelCount.keys():
            labelCount[classify] = 1
        else:
            labelCount[classify] += 1
    H = 0.0
    for value in labelCount.values():
        pi = value / numOfData
        H -= pi * log(pi, 2)
    return H


def chooseBestFeatureSplit(dataSet):
    HD = shannoEnt(dataSet)
    bestGain = 0.0
    bestFeature = -1
    for i in range(len(dataSet[0]) - 1):
        feat = [data[i] for data in dataSet]
        prob = 0.0
        for value in set(feat):
            subData = splitData(dataSet, i, value)
            prob += (len(subData) / len(dataSet)) * shannoEnt(subData)
        Gain = HD - prob
        if Gain > bestGain:
            bestGain = Gain
            bestFeature = i
    # print('最優特徵是:',label[bestFeature])
    return bestFeature


def splitData(dataSet, axis, value):
    retDataSet = []
    for data in dataSet:
        if data[axis] == value:
            reducedData = data[:axis]
            reducedData.extend(data[axis + 1:])
            retDataSet.append(reducedData)
    return retDataSet


def majorityCnt(classList):
    classCount = dict([(i, classList.count(i)) for i in classList])
    return max(classCount, key=lambda x: classCount[x])


def createTree(dataSet, labels):
    classList = [example[-1] for example in dataSet]
    if len(set(classList)) == 1:
        return classList[0]
    if len(dataSet[0]) == 0:
        return majorityCnt(classList)
    bestFeat = chooseBestFeatureSplit(dataSet)
    bestFeatLabel = labels[bestFeat]

    # 建立樹:
    myTree = {bestFeatLabel: {}}

    del (labels[bestFeat])

    featValues = [example[bestFeat] for example in dataSet]



    for value in set(featValues):
        subLabels = labels[:]
        subDataSet = splitData(dataSet, bestFeat, value)

        myTree[bestFeatLabel][value] = createTree(subDataSet, subLabels)
    for key, value in myTree.items():
        print(key, value)
    return myTree






dataSet, label = loadDataSet()

mytree=createTree(dataSet, label)
# print(mytree)

# print('*' * 10)

def x(tree:dict, a,y,z):
  print(a,y,z)
  if type(tree) is not dict:
      a=a+1
      print(tree,a)
      return

  if len(tree) > 1:

    for value in tree.values():
        y=y+1
        x(value,a,y,z)
  else:
    z=z+1
    print(tree.keys())
    x(list(tree.values())[0],a,y,z)

def y(tree:dict):
    if type(tree) is not dict:
        print(tree)
        return
    if len(tree)>1:
        for value in tree.values():
            y(value)
            # print(value)
    else:
        # print(tree.values())
        y(list(tree.values())[0])

        print(tree.keys())

#
# print("先序遍歷:")
# x(mytree,0,0,0)
# print("後序遍歷:")
# y(mytree)

# coding:utf-8
'''
**************************************************
@File   :myPycharm -> treePlotter
@IDE    :PyCharm
@Author :20192320楊坤
@Date   :2022/5/25 9:01
**************************************************
'''

import matplotlib.pyplot as plt

# 定義文字框和箭頭格式
decisionNode = dict(boxstyle="round4", color='#3366FF')  #定義判斷結點形態
leafNode = dict(boxstyle="circle", color='#FF6633')  #定義葉結點形態
arrow_args = dict(arrowstyle="<-", color='g')  #定義箭頭

#繪製帶箭頭的註釋
def plotNode(nodeTxt, centerPt, parentPt, nodeType):
    createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction',
                            xytext=centerPt, textcoords='axes fraction',
                            va="center", ha="center", bbox=nodeType, arrowprops=arrow_args)


#計算葉結點數
def getNumLeafs(myTree):
    numLeafs = 0
    firstStr = list(myTree.keys())[0]
    secondDict = myTree[firstStr]
    for key in secondDict.keys():
        if type(secondDict[key]).__name__ == 'dict':
            numLeafs += getNumLeafs(secondDict[key])
        else:
            numLeafs += 1
    return numLeafs


#計算樹的層數
def getTreeDepth(myTree):
    maxDepth = 0
    firstStr = list(myTree.keys())[0]
    secondDict = myTree[firstStr]
    for key in secondDict.keys():
        if type(secondDict[key]).__name__ == 'dict':
            thisDepth = 1 + getTreeDepth(secondDict[key])
        else:
            thisDepth = 1
        if thisDepth > maxDepth:
            maxDepth = thisDepth
    return maxDepth


#在父子結點間填充文字資訊
def plotMidText(cntrPt, parentPt, txtString):
    xMid = (parentPt[0] - cntrPt[0]) / 2.0 + cntrPt[0]
    yMid = (parentPt[1] - cntrPt[1]) / 2.0 + cntrPt[1]
    createPlot.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30)


def plotTree(myTree, parentPt, nodeTxt):
    numLeafs = getNumLeafs(myTree)
    depth = getTreeDepth(myTree)
    firstStr = list(myTree.keys())[0]
    cntrPt = (plotTree.xOff + (1.0 + float(numLeafs)) / 2.0 / plotTree.totalW, plotTree.yOff)
    plotMidText(cntrPt, parentPt, nodeTxt)  #在父子結點間填充文字資訊
    plotNode(firstStr, cntrPt, parentPt, decisionNode)  #繪製帶箭頭的註釋
    secondDict = myTree[firstStr]
    plotTree.yOff = plotTree.yOff - 1.0 / plotTree.totalD
    for key in secondDict.keys():
        if type(secondDict[key]).__name__ == 'dict':
            plotTree(secondDict[key], cntrPt, str(key))
        else:
            plotTree.xOff = plotTree.xOff + 1.0 / plotTree.totalW
            plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)
            plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))
    plotTree.yOff = plotTree.yOff + 1.0 / plotTree.totalD


def createPlot(inTree):
    fig = plt.figure(1, facecolor='white')
    fig.clf()
    axprops = dict(xticks=[], yticks=[])
    createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)
    plotTree.totalW = float(getNumLeafs(inTree))
    plotTree.totalD = float(getTreeDepth(inTree))
    plotTree.xOff = -0.5 / plotTree.totalW;
    plotTree.yOff = 1.0;
    plotTree(inTree, (0.5, 1.0), '')
    plt.savefig('a.jpg')
    plt.show()
# coding:utf-8
'''
**************************************************
@File   :myPycharm -> main
@IDE    :PyCharm
@Author :20192320楊坤
@Date   :2022/5/25 9:02
**************************************************
'''


from pylab import *
from sympy.physics.quantum.tests.test_circuitplot import mpl

import treePlotter
from ex_03 import *
mpl.rcParams['font.sans-serif'] = ['SimHei']  # 指定預設字型
mpl.rcParams['axes.unicode_minus'] = False  # 解決儲存影象時負號'-'顯示為方塊的問題
##################################

# 測試決策樹的構建
myDat, labels = loadDataSet()
myTree = createTree(myDat, labels)
# 繪製決策樹

treePlotter.createPlot(myTree)

5.執行截圖:


6.實驗過程中遇到的問題和解決過程

  • 問題1.雲伺服器配置python環境
  • 解決辦法:通過在雲伺服器配置anaconda軟體來配置python環境3.8,同時需要修改環境變數。還可以在雲伺服器中建立虛擬環境,這樣就可以在物理機pycharm中執行程式碼。
  • 問題2.雲伺服器畫圖的圖片沒有字型,同時報錯沒有找到‘sans-serif’字型
  • 解決辦法:先找到python字型庫的位置,下載對應字型後傳到指定資料夾
  • 問題3.雲伺服器不能執行rar壓縮檔案
  • 解決辦法:在雲伺服器下載rarlab軟體即可。

7.課程感悟與思考

1.這學期的python課程,總體來說收穫很多,感謝帥氣的王老師的教學。
2.從第一節課初步認識python,我知道了python的優點:可讀性強、編寫簡單、功能強大、擴充套件性良好。
3.再到後面詳細學習python的基礎語言規範:資料型別、迴圈、條件、序列、字典、異常等等,這些都讓我收穫很多。同時python強大的擴充套件庫例如:numpy、matplotlib等等在機器學習上使用更加方便。這也讓我對於python的學習更加感興趣。
4.對於更高階的學習:正則表示式、網路程式設計、資料庫操作,這些雖然都是隻是簡單初步的入門,但是程式的編寫思想都是共通的,在課堂之外自己深入學習,也是收穫滿滿。
5.python中最為經典的應用,爬蟲。這是我第一次接觸到爬蟲,結合到學過的web的知識,學起來也是很容易上手。這一塊可以在今後繼續深入的理解學習。

8.課程的建議:

1.對於雲伺服器的使用可以在課程一開始就增加一節課詳細介紹雲伺服器,這樣也會使得整個課程更加飽滿,同時這也會成為課程的一大特色。
2.課程中期的測評可以保留:既增加了課程的豐富程度又可以帶來一些獎勵。。。