白板推导系列Pytorch-朴素贝叶斯
下面的实现基于极大似然估计,意思就是使用极大似然估计的方法来估计模型中的未知参数,因此我们当然能想到还有其它的估计方法,例如贝叶斯估计。如果您仍然不明白我在说什么,或许是您还没有看白板推导的系列视频
pytorch实现
生成数据集
def create_dataset(n_samples=1000):
x0 = torch.normal(2,1,size=(n_samples//2,2),dtype=torch.float32)
y0 = torch.zeros(n_samples//2,dtype=torch.float32)
x1 = torch.normal(-2,1,size=(n_samples-n_samples//2,2),dtype=torch.float32)
y1 = torch.ones(n_samples-n_samples//2,dtype=torch.float32)
#合并数据x,y
x=torch.cat((x0,x1),0)
y=torch.cat((y0,y1),0)
return x,y
定义模型
class NaiveBayes:
def __init__(self,lamda) -> None:
self.lamda = lamda
def fit(self,X_data,y_data):
self.classes = list(set(y_data))
# 求先验
self.prior = []
for i in self.classes:
self.prior.append((y_data==i).sum().item()/len(y_data))
# 按照分类分离样本,主要是为了在预测的时候更方便求样本数和指定特征值的样本数
self.samples = []
for i in self.classes:
self.samples.append(X_data[y_data==i,:])
# 在拉普拉斯平滑的时候,需要用到特征值的种数
self.feature_value_count = []
for i in range(X_data.shape[1]):
self.feature_value_count.append(len(list(set(X_data[:,i]))))
# 预测
def predict(self,X_data):
pred = []
for i in range(X_data.shape[0]):
post = []
# 对每一个分类分别求概率
for j in range(len(self.classes)):
# 获得先验
prior = self.prior[j]
# 对每个特征求似然
likelihood = 1.0
for k in range(X_data.shape[1]):
likelihood *= ((self.samples[j][:,k]==X_data[i][k]).sum().item()+self.lamda)/(self.samples[j].shape[0]+self.lamda*self.feature_value_count[k])
# 获得后验概率
post.append(prior*likelihood)
post = torch.tensor(post)
# 将最大后验概率对应的下标作为分类
pred.append(self.classes[torch.argmax(post).item()])
return torch.tensor(pred)
# 评估
def score(self,X_data,y_data):
pred = self.predict(X_data)
return (pred==y_data).sum().item()/len(y_data)
测试
X,y = create_dataset(1000)
nb = NaiveBayes(lamda=0)
nb.fit(X[:800],y[:800])
nb.score(X[800:],y[800:])
numpy实现
不知道为啥,pytorch版本实现的代码速度非常的慢,我在问答区发布了这个问题,如果大家有什么想法,欢迎大家在评论区或问答区发表。
numpy实现的全部代码如下,跟pytorch版本除了API以外基本一致,但速度很快,大家可以探究一下原因
import numpy as np
class NaiveBayes:
def __init__(self, lamda) -> None:
self.prior = []
self.samples = []
self.feature_value_count = []
self.lamda = lamda
def fit(self, X_data, y_data):
self.classes = list(set(y_data))
# 求先验
for i in self.classes:
self.prior.append((y_data == i).sum() / len(y_data))
# 按照分类分离样本
for i in self.classes:
self.samples.append(X_data[y_data == i, :])
for i in range(X_data.shape[1]):
self.feature_value_count.append(len(set(X_data[:, i])))
# 预测
def predict(self, X_data):
pred = []
for i in range(X_data.shape[0]):
post = []
# 对每一个分类分别求概率
for j in range(len(self.classes)):
# 获得先验
prior = self.prior[j]
# 对每个特征求似然
likelihood = 1.0
for k in range(X_data.shape[1]):
likelihood *= ((self.samples[j][:, k] == X_data[i][k]).sum().item() + self.lamda) / (
self.samples[j].shape[0] + self.lamda * self.feature_value_count[k])
# 获得后验概率
post.append(prior * likelihood)
# 将最大后验概率对应的下标作为分类
pred.append(self.classes[np.argmax(post)])
return np.array(pred)
# 评估
def score(self, X_data, y_data):
pred = self.predict(X_data)
return (pred == y_data).sum().item() / len(y_data)
def create_dataset(n_samples=1000):
x0 = np.random.normal(2, 1, size=(n_samples // 2, 2))
y0 = np.zeros(n_samples // 2)
x1 = np.random.normal(-2, 1, size=(n_samples - n_samples // 2, 2))
y1 = np.ones(n_samples - n_samples // 2)
# 合并数据x,y
x = np.vstack((x0, x1))
y = np.hstack((y0, y1))
return x, y
X, y = create_dataset(1000)
nb = NaiveBayes(lamda=0.1)
nb.fit(X[:800], y[:800])
print(nb.score(X[800:], y[800:]))