kNN算法
kNN算法的原理很簡單,就是將新數據的特徵與樣本集中相應的特徵進行比較,而後提取將樣本集中特徵最類似的數據的分類標籤做爲新數據的標籤,通常的,只選取數據集中前k個最類似的元素,所以該算法被稱爲kNN,一般k取不大於20的整數。數組
下面看書上給出的實例:app
from numpy import *ide
import operator函數
def createdataset():spa
group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])對象
labels=['a','a','b','b']排序
return group,labels索引
group,labels=createdataset()事務
def classify(inx,dataset,labels,k):
datasetsize=dataset.shape[0]
diffmat=tile(inx,(datasetsize,1)) - dataset
sqdiffmat=diffmat**2
sqdistance=sqdiffmat.sum(axis=1)
distance=sqdistance**0.5 #計算距離,也就是比較特徵
sorteddist=distance.argsort()
classcount={}
for i in range(k):
votelabel=labels[sorteddist[i]]
classcount[votelabel]=classcount.get(votelabel,0)+1 #爲字典classcount建立前k個最近的數據的標籤對應出現次數的鍵值對
sortedclasscount=sorted(classcount.items(),key=operator.itemgetter(1),reverse=True)
return sortedclasscount[0][0]
print(classify([0,0],group,labels,3))
首先,定義了一個createdataset()函數用來生成數據樣本集,而後定義的classify()函數有4個輸人蔘數:用於分類的輸人向量是inx,輸入的訓練樣本集爲dataset, 標籤向量爲labels 最後的參數k表示用於選擇最近鄰居的數目其中標籤向量的元素數目和矩 陣如dataset的行數相同。
dataset.shape[0]返回dataset數組的第二維,也就是二維數組的行數,此例中爲4,接下來的tile函數將inx數組的第二維重複了四次,第一維重複一次,也就是將inx數組從一行變成了4行,每一行都是原來的inx數組,這樣就與dataset數組的行列數是同樣的,用於接下來計算inx與每一個數據樣本的距離,這裏的sqdiffmat.sum(axis=1)表示每一行的數組求和,這裏返回一個長度爲4的一維數組,每個元素都是sqdiffmat對應一行的和,下面的argsort()函數將distance中的元素從小到大排列,提取其對應的index(索引),而後輸出到sorteddist。
而後建立了一個空字典classcount,在接下來的for循環中爲字典classcount建立了前k個最近的數據的標籤對應出現次數的鍵值對
接下來有一個sorted函數,它的語法以下:
sorted(iterable, key=None, reverse=False)
iterable – 可迭代對象。
key – 主要是用來進行比較的元素,只有一個參數,具體的函數的參數就是取自於可迭代對象中,指定可迭代對象中的一個元素來進行排序。
reverse – 排序規則,reverse = True 降序 , reverse = False 升序(默認)。
items函數返回字典中可遍歷的(鍵, 值) 元組數組。
operator.itemgetter函數的做用是獲取對象哪些維的數據,參數是表示維的序號。
在這個例子中這個sorted函數的做用就是對字典classcount所生成的元組數組按照標籤對應出現的次數進行降序排序,最後整個classify函數返回的sortedclasscount[0][0]就是出現次數最多的標籤,以此做爲待劃分數據的分類標籤。最後運行結果爲 b 。
決策樹
決策樹僞代碼以下:
檢測數據集中的每一個子項是否屬於同一分類:
If so return 類標籤;
Else
尋找劃分數據集的最好特徵
劃分數據集
建立分支節點
for 每一個劃分的子集
調用決策樹函數並增長返回結果到分支節點中
return 分支節點
下面根據僞代碼一步一步實現
熵
在劃分數據集以前以後信息發生的變化稱爲信息增益,知道如何計算信息增益,咱們就能夠 計算每一個特徵值劃分數據集得到的信息增益,得到信息增益最高的特徵就是最好的選擇,集合信息的度量方式稱爲香農熵或者簡稱爲熵,熵定義爲信息的指望值。
若是待分類的事務可能劃分在多個分類之中, 則符號x的信息定義爲 l(x)=-lb(p(x)),p(x)爲選擇該分類的機率。
要計算熵,則須要計算全部類別全部可能值包含的信息指望值。下面給出書上的計算熵代碼:
from math import log
def c_shannon_ent(data_set):
num_entries=len(data_set)
label_counts={}
for feat_vec in data_set:
current_label=feat_vec[-1]
if current_label not in label_counts.keys():
label_counts[current_label]=0
label_counts[current_label]+=1
shannon_ent=0.0
for key in label_counts:
prob=float(label_counts[key])/num_entries
shannon_ent-=prob*log(prob,2)
return shannon_ent
def create_data_set():
data_set=[[1,1,'yes'],[1,0,'no'],[0,1,'no'],[1,1,'yes'],[0,1,'no']]
labels=['no surfacing','flippers']
return data_set,labels
my_data,labels=create_data_set()
print(c_shannon_ent(my_data))
keys()函數用於返回一個字典全部的鍵。不難看出label_counts是一個將類別與屬於該類別的樣本數做爲鍵值對的字典。實際上,對於data_set上的一個隨機變量x,x爲yes所屬類的機率爲2/5,x爲no的機率爲3/5,那麼x的熵爲-(2/5)*lb(2/5)-(3/5)*lb(3/5),也就是上面代碼中計算的data_set的熵。(在這個實例中,yes和no對應的是最終分類標籤(是否爲魚),前面的1,0是用於判斷的特徵數據,因此按照yes和no的分類來計算data_set的熵)熵越高,表明數據的無序程度就越高。
劃分數據集
先看看書上的對於給定特徵劃分數據集的代碼:
def split_data_set(data_set,axis,value):
ret_data_set=[]
for feat_vec in data_set:
if feat_vec[axis]==value:
reduced_feat_vec=feat_vec[:axis]
reduced_feat_vec.extend(feat_vec[axis+1:])
ret_data_set.append(reduced_feat_vec)
return ret_data_set
這段代碼仍是很容易看懂的,首先建立了一個新列表,而後對於符合所輸入的特徵的數據就截取到 reduced_feat_vec中(把特徵值截掉了),而後再將 reduced_feat_vec添入新建的列表ret_data_set中做爲一個元素,這樣就獲得了一個符合所選特徵值的數據集ret_data_set。
選擇最好的數據集劃分方式
def choose_best_feature_to_split(data_set):
num_features=len(data_set[0])-1
base_entropy=c_shannon_ent(data_set)
best_info_gain=0.0;best_feature=-1
for i in range(num_features):
feat_list=[example[i] for example in data_set]
unique_vals=set(feat_list)
new_entropy=0.0
for value in unique_vals:
sub_data_set=split_data_set(data_set,i,value)
prob=len(sub_data_set)/float(len(data_set))
new_entropy+=prob*c_shannon_ent(sub_data_set)
info_gain=base_entropy-new_entropy
if(info_gain>best_info_gain):
best_info_gain=info_gain
best_feature=i
return best_feature
若是上面都看懂了,那麼看懂這一段代碼也不會難的。
首先用一個變量存儲特徵總數,一個變量存儲初始熵,而後每選一個特徵,就用一個集合unique_vals存儲該特徵全部可能的取值,再根據這個對該特徵進行分類而且計算分類後的熵,最後計算出信息增益info_gain,而後從全部特徵值中找出信息增益最大的一個,輸出該特徵值。爲何找信息增益最大的呢?上面說到在劃分數據集以前以後信息發生的變化稱爲信息增益,也就是劃分先後的熵之差,而熵越高,代表無序程度越大,信息增益實際上就是無序程度減小的程度,越大就代表分類後的無序程度越小。
構建決策樹
def major_cnt(class_list):
class_count={}
for vote in class_list:
if vote not in class_count.keys():class_count[vote]=0
class_count[vote]+=1
sorted_class_count=sorted(class_count.items,key=operator.itemgetter,reverse=True)
return sorted_class_count[0][0]
def create_tree(data_set,labels):
class_list=[example[-1] for example in data_set]
if class_list.count(class_list[0])==len(class_list):
return class_list[0]
if len(data_set[0])==1:
return major_cnt(class_list)
best_feat=choose_best_feature_to_split(data_set)
best_feat_label=labels[best_feat]
my_tree={best_feat_label:{}}
del(labels[best_feat])
feat_values=[example[best_feat] for example in data_set]
unique_vals=set(feat_values)
for value in unique_vals:
sub_labels=labels[:]
my_tree[best_feat_label][value]=create_tree(split_data_set(data_set,best_feat,value),sub_labels)
return my_tree
第一個函數和Knn裏的一段代碼是很類似的,這裏再也不多說。
第二個函數有兩個參數:數據集data_set和標籤labels,標籤對該算法來講不是必須的,只是能代表數據的內在乎義。
create_tree中第一個if表示若是數據都是同一類就返回該類別,第二個if表示特徵用完後數據仍然不是同一類則返回出現次數最多的分類標籤。而後就是選取最好的特徵進行分類,再對分好類的數據集執行一樣的操做,最後生成整顆樹,代碼仍是至關簡單的。
最後給出完整的代碼:鄭州人流價格 http://www.zzzykdfk.com/
from math import log
import operator
def c_shannon_ent(data_set):
num_entries=len(data_set)
label_counts={}
for feat_vec in data_set:
current_label=feat_vec[-1]
if current_label not in label_counts.keys():
label_counts[current_label]=0
label_counts[current_label]+=1
shannon_ent=0.0
for key in label_counts:
prob=float(label_counts[key])/num_entries
shannon_ent-=prob*log(prob,2)
return shannon_ent
def create_data_set():
data_set=[[1,1,'yes'],[1,0,'no'],[0,1,'no'],[1,1,'yes'],[0,1,'no']]
labels=['no surfacing','flippers']
return data_set,labels
def split_data_set(data_set,axis,value):
ret_data_set=[]
for feat_vec in data_set:
if feat_vec[axis]==value:
reduced_feat_vec=feat_vec[:axis]
reduced_feat_vec.extend(feat_vec[axis+1:])
ret_data_set.append(reduced_feat_vec)
return ret_data_set
def choose_best_feature_to_split(data_set):
num_features=len(data_set[0])-1
base_entropy=c_shannon_ent(data_set)
best_info_gain=0.0;best_feature=-1
for i in range(num_features):
feat_list=[example[i] for example in data_set]
unique_vals=set(feat_list)
new_entropy=0.0
for value in unique_vals:
sub_data_set=split_data_set(data_set,i,value)
prob=len(sub_data_set)/float(len(data_set))
new_entropy+=prob*c_shannon_ent(sub_data_set)
info_gain=base_entropy-new_entropy
if(info_gain>best_info_gain):
best_info_gain=info_gain
best_feature=i
return best_feature
def major_cnt(class_list):
class_count={}
for vote in class_list:
if vote not in class_count.keys():class_count[vote]=0
class_count[vote]+=1
sorted_class_count=sorted(class_count.items,key=operator.itemgetter,reverse=True)
return sorted_class_count[0][0]
def create_tree(data_set,labels):
class_list=[example[-1] for example in data_set]
if class_list.count(class_list[0])==len(class_list):
return class_list[0]
if len(data_set[0])==1:
return major_cnt(class_list)
best_feat=choose_best_feature_to_split(data_set)
best_feat_label=labels[best_feat]
my_tree={best_feat_label:{}}
del(labels[best_feat])
feat_values=[example[best_feat] for example in data_set]
unique_vals=set(feat_values)
for value in unique_vals:
sub_labels=labels[:]
my_tree[best_feat_label][value]=create_tree(split_data_set(data_set,best_feat,value),sub_labels)
return my_tree
data,labels=create_data_set()
print(create_tree(data,labels))
輸出結果:{‘no surfacing’:{0:‘no’,1:{‘flippers’:{0:‘no’,1:‘yes’}}}}