60行代码徒手实现深度神经网络

文章发布于公号【数智物语】 (ID:decision_engine),关注公号不错过每一篇干货。

来源 | Python与算法之美(id:Python_Ai_Road)

01

准备数据集

采用的数据集是sklearn中的breast cancer数据集,30维特征,569个样本。训练前进行MinMax标准化缩放至[0,1]区间。按照75/25比例划分成训练集和验证集。

# 获取数据集

importnumpyasnp

importpandasaspd

fromsklearnimportdatasets

fromsklearnimportpreprocessing

fromsklearn.model_selectionimporttrain_test_split

breast = datasets.load_breast_cancer()

scaler = preprocessing.MinMaxScaler()

data = scaler.fit_transform(breast['data'])

target = breast['target']

X_train,X_test,y_train,y_test = train_test_split(data,target)

02

模型结构图

03

正反传播公式

04

NN实现代码

importnumpyasnp

importpandasaspd

#定义激活函数

ReLu =lambdaz:np.maximum(0.0,z)

d_ReLu =lambdaz:np.where(z<0,0,1)

LeakyReLu =lambdaz:np.maximum(0.01*z,z)

d_LeakyReLu =lambdaz:np.where(z<0,0.01,1)

Sigmoid =lambdaz:1/(1+np.exp(-z))

d_Sigmoid =lambdaz: Sigmoid(z)*(1-Sigmoid(z))#d_Sigmoid = a(1-a)

Tanh = np.tanh

d_Tanh =lambdaz:1- Tanh(z)**2#d_Tanh = 1 - a**2

classNNClassifier(object):

def__init__(self,n = [np.nan,5,5,1],alpha =0.1,ITERNUM =50000, gfunc ='ReLu'):

self.n = n#各层节点数

self.gfunc = gfunc#隐藏层激活函数

self.alpha,self.ITERNUM = alpha,ITERNUM

self.dfJ = pd.DataFrame(data = np.zeros((ITERNUM,1)),columns = ['J'])

self.W,self.b = np.nan,np.nan

# 确定各层激活函数

self.g = [eval(self.gfunc)foriinrange(len(n))];

self.g[-1] = Sigmoid;self.g[0] = np.nan

# 确定隐藏层激活函数的导数

self.d_gfunc = eval('d_'+ self.gfunc)

deffit(self,X_train,y_train):

X,Y = X_train.T,y_train.reshape(1,-1)

m = X.shape[1]#样本个数

n = self.n; n[0] = X.shape[0]# 各层节点数量

# 节点值和参数初始化

A = [np.zeros((ni,m))forniinn];A[0] = X#各层节点输出值初始化

Z = [np.zeros((ni,m))forniinn];Z[0] = np.nan#各层节点中间值初始化

W = [np.nan] + [np.random.randn(n[i],n[i-1]) *0.01foriinrange(1,len(n))]#各层系数参数

b = [np.zeros((ni,1))forniinn];b[0] = np.nan#n各层偏置参数

# 导数初始化

dA = [np.zeros(Ai.shape)forAiinA]

dZ = [np.zeros(Ai.shape)forAiinA]

dW = [np.zeros(Wi.shape)ifisinstance(Wi,np.ndarray)elsenp.nanforWiinW]

db = [np.zeros(bi.shape)ifisinstance(bi,np.ndarray)elsenp.nanforbiinb]

forkinrange(self.ITERNUM):

# ---------正向传播 ----------

foriinrange(1,len(n)):

Z[i] = np.dot(W[i],A[i-1]) + b[i]

A[i] = self.g[i](Z[i])

J = (1/m) * np.sum(- Y*np.log(A[len(n)-1]) -(1-Y)*np.log(1-A[len(n)-1]))

self.dfJ.loc[k]['J']= J

# ----------反向传播 ---------

hmax = len(n) -1

dA[hmax] =1/m*(-Y/A[hmax] + (1-Y)/(1-A[hmax]))

dZ[hmax] =1/m*(A[hmax]-Y)

dW[hmax] = np.dot(dZ[hmax],A[hmax-1].T)

db[hmax] = np.dot(dZ[hmax],np.ones((m,1)))

foriinrange(len(n)-2,0,-1):

dA[i] = np.dot(W[i+1].T,dZ[i+1])

dZ[i] = dA[i]* self.d_gfunc(Z[i])

dW[i] = np.dot(dZ[i],A[i-1].T)

db[i] = np.dot(dZ[i],np.ones((m,1)))

#-----------梯度下降 ---------

foriinrange(1,len(n)):

W[i] = W[i] - self.alpha*dW[i]

b[i] = b[i] - self.alpha*db[i]

# 显示进度

if(k+1)%1000==0:

print('progress rate:{}/{}'.format(k+1,self.ITERNUM),end ='\r')

self.W,self.b = W,b

defpredict_prob(self,X_test):

# ---------正向传播 ----------

W,b = self.W,self.b

Ai = X_test.T

foriinrange(1,len(self.n)):

Zi = np.dot(W[i],Ai) + b[i]

Ai = self.g[i](Zi)

return(Ai.reshape(-1))

defpredict(self,X_test):

Y_prob = self.predict_prob(X_test)

Y_test = Y_prob.copy()

Y_test[Y_prob>=0.5] =1

Y_test[Y_prob<0.5] =0

return(Y_test)

05

单隐层神经网络

设置1个隐藏层,隐藏层节点数为5,隐藏层使用Sigmoid激活函数。

# 采用Sigmoid激活函数

NN = NNClassifier(n = [np.nan,5,1],alpha =0.02,

ITERNUM =200000, gfunc ='Sigmoid')

NN.fit(X_train,y_train)

# 绘制目标函数迭代曲线

%matplotlib inline

NN.dfJ.plot(figsize = (12,8))

# 测试在验证集的auc得分

fromsklearn.metricsimportroc_auc_score

Y_prob = NN.predict_prob(X_test)

roc_auc_score(list(y_test),list(Y_prob))

隐藏层使用Tanh激活函数。

# 采用 Tanh激活函数

NN = NNClassifier(n = [np.nan,5,1],alpha =0.02,

ITERNUM =200000, gfunc ='Tanh')

NN.fit(X_train,y_train)

# 绘制目标函数迭代曲线

%matplotlib inline

NN.dfJ.plot(figsize = (12,8))

# 测试在验证集的auc得分

fromsklearn.metricsimportroc_auc_score

Y_prob = NN.predict_prob(X_test)

roc_auc_score(list(y_test),list(Y_prob))

隐藏层使用ReLu激活函数。

# 采用 ReLu激活函数

NN = NNClassifier(n = [np.nan,5,1],alpha =0.02,

ITERNUM =200000, gfunc ='ReLu')

NN.fit(X_train,y_train)

# 绘制目标函数迭代曲线

%matplotlib inline

NN.dfJ.plot(figsize = (12,8))

# 测试在验证集的auc得分

fromsklearn.metricsimportroc_auc_score

Y_prob = NN.predict_prob(X_test)

roc_auc_score(list(y_test),list(Y_prob))

隐藏层使用LeakyReLu激活函数。

# 采用 LeakyReLu激活函数

NN = NNClassifier(n = [np.nan,5,1],alpha =0.02,

ITERNUM =200000, gfunc ='LeakyReLu')

NN.fit(X_train,y_train)

# 绘制目标函数迭代曲线

%matplotlib inline

NN.dfJ.plot(figsize = (12,8))

# 测试在验证集的auc得分

fromsklearn.metricsimportroc_auc_score

Y_prob = NN.predict_prob(X_test)

roc_auc_score(list(y_test),list(Y_prob))

以上试验似乎表明,在当前的数据集上,隐藏层采用ReLu激活函数是一个最好的选择,AUC最高得分为0.99958。

06

双隐层神经网络

设置2个隐藏层,隐藏层节点数都为5,隐藏层都使用ReLu激活函数。

# 设置两个隐藏层,采用ReLu激活函数

NN = NNClassifier(n = [np.nan,5,5,1],alpha =0.02,

ITERNUM =200000, gfunc ='ReLu')

NN.fit(X_train,y_train)

# 绘制目标函数迭代曲线

%matplotlib inline

NN.dfJ.plot(figsize = (12,8))

# 测试在验证集的auc得分

fromsklearn.metricsimportroc_auc_score

Y_prob = NN.predict_prob(X_test)

roc_auc_score(list(y_test),list(Y_prob))

AUC得分0.99874比采用单隐藏层的最优得分0.99958有所降低,可能是模型复杂度过高,我们尝试减少隐藏层节点的个数至3以降低模型复杂度。

# 双隐藏层,隐藏层节点数为3

NN = NNClassifier(n = [np.nan,3,3,1],alpha =0.02,

ITERNUM =200000, gfunc ='ReLu')

NN.fit(X_train,y_train)

# 绘制目标函数迭代曲线

%matplotlib inline

NN.dfJ.plot(figsize = (12,8))

# 测试在验证集的auc得分

fromsklearn.metricsimportroc_auc_score

Y_prob = NN.predict_prob(X_test)

roc_auc_score(list(y_test),list(Y_prob))

AUC得分0.99979,又有所提高。

和sklearn中自带的神经网络分类器进行对比。

# 和sklearn中的模型对比

fromsklearn.neural_networkimportMLPClassifier

# 第一隐藏层神经元个数为3,第二隐藏层神经元个数为3

MLPClf = MLPClassifier(hidden_layer_sizes=(3,3),max_iter=200000,activation='relu')

MLPClf.fit(X_train,y_train)

# 绘制目标函数迭代曲线

dfJ = pd.DataFrame(data = np.array(MLPClf.loss_curve_),columns = ['J'])

dfJ.plot(figsize = (12,8))

# 测试在验证集的auc得分

fromsklearn.metricsimportroc_auc_score

Y_prob = MLPClf.predict_proba(X_test)[:,1]

roc_auc_score(list(y_test),list(Y_prob))

以上试验表明,针对当前数据数据集,选择ReLu激活函数,采用双隐藏层,每个隐藏层节点数设置为3是一个不错的选择,AUC得分为0.99979。该得分高于采用CV交叉验证优化超参数后的逻辑回归模型的0.99897的AUC得分。

星标我,每天多一点智慧

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容