手写xgboost
Posted 出尘呢
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写xgboost相关的知识,希望对你有一定的参考价值。
# 训练
# 1)输入n*dx的numpy矩阵,n个样本,dx个特征。
import numpy as np
x = np.loadtxt("input/train.txt", dtype=np.float64)
y = np.loadtxt("label/train.txt", dtype=np.float64)
print('loaded')
n,dx=x.shape
_,dy=y.shape
# 2)弄一个输出表n*dy,是xgboost的输出,置0,同时还有gihi表
output=np.zeros_like(y) # 定义n*dy的output表,初始化为0。
from base.lib_grad_hess import func_grad_hess
grad,hess=func_grad_hess(output,y) # 定义n*dy的gi和hi表grad,hess,按照softmax_output计算。
print('pre down')
##### 树的定义#####
class Tree(object):
def __init__(self):
self.root=Node()
def train(self,n,dyi):
self.root.id_list = list(range(n))
self.root.dfs(dyi,list(range(dx)),0)
def test(self,n_v,dyi):
self.root.id_list = list(range(n_v))
self.root.test_dfs(dyi)
import random
import math
from base.lib_hypara import INF
# https://zhuanlan.zhihu.com/p/35061092
lam=1 # L2正则化权重项
gam=0.1 # 指定叶节点个数的正则化权重
max_depth=5 # 指定树的最大深度
min_sample=3 # 最小样本数在3左右
min_child_weight = 2 # 最小样本数改成样本的hi和,min_child_weight
min_gain=0.00 # 最小增益
lr=0.1 # 学习率
class Node(object):
def __init__(self):
self.is_leaf=None
self.leaf_weight=None
self.left = None
self.right = None
self.id_list = []
self.classify = None
def dfs(self,dyi,chra_list,depth): # dyi 是第dyi个标签
# 这是一个判断分裂函数
# 4)直到: 到达最大深度 or 当类的样本个数低于阈值 or 当纯度分数的提高低于阈值,则此节点不分裂是叶节点。
G, H = 0., 0.
for i in self.id_list:
G += grad[i][dyi]
H += hess[i][dyi]
flag = False # 是不是叶子
# if depth>=max_depth or H < min_child_weight:
# flag = True
# if H < min_child_weight:
# print('h:',H,len(self.id_list))
# else:
# print('depth:',depth,len(self.id_list))
if depth>=max_depth or len(self.id_list) < min_sample:
flag = True
if len(self.id_list) < min_sample:
print('m:',len(self.id_list))
else:
print('depth:',depth,len(self.id_list))
else:
gain,max_classify,G,H=self.split_finding(dyi, chra_list)
if gain<min_gain:
flag=True
print('gain:', gain, len(self.id_list))
if flag:
# 是叶节点
self.is_leaf=True
# w ∗ j = − P i∈Ij gi P i∈Ij hi + λ ,
self.leaf_weight=-G/(H+lam)
# 5)计算叶节点的权值w=w'*lr。
self.leaf_weight*=lr
# 6)输出表添加权值w
for i in self.id_list:
output[i][dyi]+=self.leaf_weight
else:
# 是分支节点
self.is_leaf=False
# 分裂依据
self.classify=max_classify
# 3)若分裂,按照确定了的依据,分样本给子节点。缺失值INF单独处理。
self.func_classify()
# 接下来不能用这个特征分类
chra_list.remove(self.classify[0])
# 进入子树
self.left.dfs(dyi,chra_list,depth+1)
self.right.dfs(dyi,chra_list,depth+1)
# 把self.id_list清空减少内存
self.id_list=[]
def test_dfs(self,dyi):
if self.is_leaf:
for i in self.id_list:
output_v[i][dyi]+=self.leaf_weight
self.id_list=[]
else:
self.func_classify()
self.left.test_dfs(dyi)
self.right.test_dfs(dyi)
def func_classify(self):
# 得到左右子树
if self.left==None:self.left = Node()
if self.right==None:self.right = Node()
# 一个个分
k,v,b=self.classify
for i in self.id_list:
# 缺失值
if x[i][k]==INF:
if b == 0:
self.left.id_list.append(i)
else:
self.right.id_list.append(i)
# 非缺失
else:
if x[i][k]<=v:
self.left.id_list.append(i)
else:
self.right.id_list.append(i)
def split_finding(self,dyi,chra_list):
# 这是一个找分裂函数
# 2)每个节点随机选择k = sqrt(len(chra_list))个特征,对每个特征进行带缺失值的分裂算法:
select_chra_list=random.sample(chra_list,math.ceil(math.sqrt(len(chra_list))))
# Algorithm 3: Sparsity-aware Split Finding
# Input: I, instance set of current node
# 是id_list
# Input: Ik = i ∈ I|xik != missing
# 这个是包含在值的,是INF与否,每一个弄一个
# Input: d, feature dimension
# 这个是select_chra_list
# gain ← 0
# gain=0 # 最大增益
max_score=0 # 最大分数,gain=score/2-gam
max_classify=None # 最大增益对应的判断条件(k,v,b)
# G ← P i∈I , gi,H ← P i∈I hi
G,H=0,0
for i in self.id_list:
G+=grad[i][dyi]
H+=hess[i][dyi]
# for k = 1 to m do
for k in select_chra_list:
# 所有样本按照第k个特征排序
def takek(elem):
return x[elem][k]
self.id_list.sort(key=takek)
# 求缺失值sum
Glack,Hlack=0,0
p=len(self.id_list)-1
while(p>=0 and takek(self.id_list[p])==INF):
Glack += grad[self.id_list[p]][dyi]
Hlack += hess[self.id_list[p]][dyi]
p-=1
p+=1 # p最终是非缺失值的长度
# GL ← 0, HL ← 0
GL,HL=0,0
# for j in sorted(Ik, ascent order by xjk) do
for pj in range(p):
# missing value goto right
# GL ← GL + gj , HL ← HL + hj
GL+=grad[self.id_list[pj]][dyi]
HL+=hess[self.id_list[pj]][dyi]
if pj+1<p and takek(self.id_list[pj])==takek(self.id_list[pj+1]): # 直到有不同
continue
v=takek(self.id_list[pj]) # 对应的值
# GR ← G − GL, HR ← H − HL
GR,HR=G-GL,H-HL
# score ← max(score, G2 L HL+λ + G2 R HR+λ − G2 H+λ )
score=GL**2/(HL+lam)+GR**2/(HR+lam)-G**2/(H+lam)
if score>max_score:
max_score=score
max_classify=(k,v,1)
# missing value goto left
GL+=Glack
HL+=Hlack
GR,HR=G-GL,H-HL
# score ← max(score, G2 L HL+λ + G2 R HR+λ − G2 H+λ )
score=GL**2/(HL+lam)+GR**2/(HR+lam)-G**2/(H+lam)
if score>max_score:
max_score=score
max_classify=(k,v,0)
GL-=Glack
HL-=Hlack
# Split and default directions with max gain
gain = max_score / 2 - gam
return gain,max_classify,G,H
#################
# 4)初始化dy个xgboost森林
classifier=[[]for i in range(dy)]
# 准确率记录
from base.lib_evaluate import evaluate_acc
acc_results=[]
################
# 新增:
# 1)输入n*dx的numpy矩阵,n个样本,dx个特征,有的连续有的离散。
import numpy as np
x_v = np.loadtxt("input/valid.txt", dtype=np.float64)
y_v = np.loadtxt("label/valid.txt", dtype=np.float64)
n_v=x_v.shape[0]
# 2)dy次通过分别的xgboost分别输出dy个n*1的输出。
output_v=np.zeros((n_v,dy))
pred_result_v=[]
from base.lib_evaluate import evaluate_acc
acc_results_v=[]
######
# 5)B轮,每轮
B = 3
for epoch in range(B):
print('epoch:',epoch)
# 1)dy次,在分别的xgboost里建一棵树,每一次建一个树:
for i in range(dy):
print('tree:', i)
# 0)建空树,把所有节点放在根节点
classifier[i].append(Tree())
# 1)从根节点开始dfs
classifier[i][-1].train(n,i)
print('caculating grad hess')
# 2)重新计算每个样本每个标签的gi,hi,通过softmax。
grad,hess=func_grad_hess(output,y) # 定义n*dy的gi和hi表grad,hess,按照softmax_output计算。
print('caculating acc')
# 评价现在的准确率怎么样
acc=evaluate_acc(y,output)
print('epoch:\\tacc:'.format(epoch,acc))
acc_results.append(acc)
###验证###
for j in range(dy):
classifier[j][epoch].test(n_v,j) # 里面修改output
# 3)n个样本的标签。
acc_v = evaluate_acc(y_v, output_v)
print('valid:',acc_v)
acc_results_v.append(acc_v)
prediction_v=np.argmax(output_v, axis=1)
pred_result_v.append(prediction_v)
print('over')
import matplotlib.pyplot as plt
plt.figure(figsize=(20, 10))
plt.plot(range(len(acc_results_v)),acc_results_v)
plt.show()
plt.gcf().savefig('figure\\\\valid.png')
# 输出成excel
import pandas as pd
df=pd.DataFrame(pred_result_v).T
df.to_csv('output/valid行样本列树数.csv')
df2=pd.DataFrame(pred_result_v[-1])
df2.to_csv('output/valid_last.csv',index=False)
############
# 保存一个输出
prediction=np.argmax(output, axis=1)
np.savetxt("output/train.txt",prediction)
import matplotlib.pyplot as plt
plt.figure(figsize=(20, 10))
plt.plot(range(len(acc_results)),acc_results)
plt.show()
plt.gcf().savefig('figure\\\\train.png')
# 4)保存xgboost树,是python对象可以jsonpickle。
from base.lib_store import serialize
print('storing')
serialize(classifier,'store\\\\classifier.bin')
print('done')
以上是关于手写xgboost的主要内容,如果未能解决你的问题,请参考以下文章
R语言构建xgboost模型:xgb.cv函数交叉验证确定模型的最优子树个数(可视化交叉验证对数损失函数与xgboost模型子树个数的关系)交叉验证获取最优子树之后构建最优xgboost模型
机器学习算法中 GBDT 和 XGBOOST 的区别有哪些?