TensorFlow 实战之实现卷积神经网络的实例讲解

本文根据最近学习TensorFlow书籍网络文章的情况,特将一些学习心得做了总结,详情如下.如有不当之处,请各位大拿多多指点,在此谢过。

一、相关性概念

1、卷积神经网络(ConvolutionNeural Network,CNN)

19世纪60年代科学家最早提出感受野(ReceptiveField)。当时通过对猫视觉皮层细胞研究,科学家发现每一个视觉神经元只会处理一小块区域的视觉图像,即感受野。20世纪80年代,日本科学家提出神经认知机(Neocognitron)的概念,被视为卷积神经网络最初的实现原型。神经认知机中包含两类神经元:S-cells和C-cells。S-cells用来抽取特征,对应我们现在主流卷积神经网络中的卷积核滤波操作;C-cells用来抗形变,对应现在的激活函数、最大池化(Max-Pooling)等操作。

一般情况下,卷积神经网络由多个卷积层构成,每个卷积层通常会进行如下操作:

(1) 图像通过多个不同的卷积核的滤波,并加偏置(bias),提取出局部特征,每一个卷积核会映射出一个新的2D图像。

(2) 将前面卷积核的滤波处理结果,进行非线性的激活函数处理。目前最常见的是使用ReLU函数,之前Sigmoid函数应用较多。

(3)多激活函数处理的结果再进行池化操作(即降采样,例如:将4*4的图片降为1*1的图片),一般会使用最大池化,保留最显著特征,并提升模型畸变容忍能力。

这几个步骤就构成了最常见的卷积层,也可以再加上一个LRN(LocalResponse Normalization,局部响应归一化层)层,现在非常流行的Trick还有BatchNormalization等。

2、池化层

3、卷积核尺寸

4、神经网络算法相关特性

4.1、优点

(1)可以高效提取特征。

当我们面对一个分类任务时,传统的机器学习算法,一般要首先明确feature和label,然后拿数据取“喂”训练模型并保存,最后测试模型的准确性。这就需要我们确定好特征,当特征数目很少就无法精确进行分类而引起欠拟合;当特征数目很多,又会在分类过程中太过于看重某个特征引起分类错误,产生过拟合。而神经网络则不需要做大量的特征工程,可以直接把数据“灌”进去而让其自身训练,自我“修正”,即可达到预期效果。   (2)数据格式更加简易

利用传统的机器学习解决分类问题时,数据不能直接“灌”进去的,需要对数据进行一些处理,譬如量纲的归一化,格式的转化等等,然而在神经网络里却不需要额外对数据做过多的处理。

(3) 参数数目的少量性

同样在面对一个分类问题时,利用传统机器学习SVM来做的话,需要涉及核函数,惩罚因子,松弛变量等等参数,而这些不同的参数组合会对模型效果产生不一样的影响,想要迅速而又准确的得到最适合模型的参数,需要对相关理论知识有深入研究,但对于一个基本的三层神经网络来说(输入-隐含-输出),只需要初始化时给每一个神经元上随机的赋予一个权重w和偏置项b,在训练过程中,这两个参数就会不断的修正,调整到最优质,使模型的误差最小。所以从这个角度来看,我们的工作效率会更佳。 4.2、缺点

如果我们加深我网络层,每一个网络层都增加神经元数量,则参数的个数将是M*N(m为网络层数,N为每层神经元个数),这样一来参数很多,引起模型复杂化,就更加不好调参,进而会更加容易导致过拟合。另外,从神经网络的反向传播的过程来看,梯度在反向传播时,不断的迭代会导致梯度越来越小,引起梯度趋近于0(梯度消失),梯度消失就使得权值无法更新,这个神经元的存在就毫无意义,很难导致收敛。尤其是在图像领域,直接使用最基本的神经网络,是不合理的。

二、卷积神经网络基本原理

1、基本阐述

现在有一图像,其尺寸大小是1000像素*1000像素且设定为黑白图像,也就是只有一个颜色通道,则一张图片就要100万个像素点,输入数据维度也是100万维。如果连接的现在隐含层大小也是同样大小(100万个隐含节点),最后将产生100万*100万即一亿万个连接。仅仅一个全连接(FullConnected Layer),就有一万亿连接的权重需要去训练,目前看,显然是不划算不现实。

通过局部连接(LocalConnect)方法优化解决:由于每一个感受野只接受一小块区域的信号,且这一小块区域内的像素是互相关联的,每一个神经元不需要接收全部像素点的信息,只需要接收局部的像素点作为输入,而后将所有这些神经元收到的局部信息综合起来,就可以得到全局信息。假设局部感受野大小是10*10,即每个隐含节点只与10*10个像素点相连,现在只需要10*100万即1亿个连接。

现在隐含层每一个节点都与10*10的像素相连,即每一个隐含层节点都拥有100个参数。假设我们的局部连接方式是卷积操作,即默认每一个隐含节点的参数都完全一样,这样参数从1亿降为100。不管图像大小是多大,一律都是这个10*10=100个参数,即卷积核尺寸,显然卷积核对缩小参数数量贡献非常大、意义非凡。因此,此时,我们不需要再担心有多少隐含节点或者图片多大,参数量只跟卷积核的大小有关,即所谓的权值共享。

总结:卷积神经网络要素是局部连接(LocalConnection)、权值共享(WeightSharing)和池化层(Pooling)中的降采样(Down-Sampling)。其中,局部连接和权值共享降低了参数量,训练复杂度被大大下降、过拟合被减轻。同时,权值共享还赋予了卷积网络对平移的容忍性,而池化层降采样则进一步降低了输出参数量,并赋予模型对轻度形变的容忍性,提供了模型的泛化能力。

2、LeNet5

1994年,大名鼎鼎的LeNet5诞生,作为最早的深层卷积神经网络之一,推动了深度学习的发展。自1998年开始,在多次成功迭代之后,由Yann LeCun完成的开拓性成果被命名为LeNet5。LeCun认为,可训练参数的卷积层是一种利用少量参数在图像的多个位置上提取相似特征的有效方式,这和直接把每个像素作为多层神经网络的输入不一样。像素不应该被使用在输入层,因为图像具有很强的空间相关性,而使用图像中独立的像素直接作为输入则利用不到这些相关性。笔者认为,这些内容比较重要。

在当时,LeNet5的特性如下:

(1)每个卷积层包含三个部分:卷积、池化和非线性激活函数;

(2)使用卷积提取空间特征;

(3)降采样(Subsample)的平均池化层(AveragePooling);

(4)双曲正切(Tanh)或S型(Sigmoid)的激活函数;

(5)MLP作为最后的分类器;

(6)层与层之间的稀疏性连接减少计算复杂度。

三、TensorFlow 实现简单的卷积网络

1、简要说明

这里使用的数据集依然是MNIST,使用两个卷积层加一个全连接层构建一个简单但非常有代表性的卷积神经网络,预计准确率约为99.2%左右。

2、实现过程

#载入MNIST数据集,创建默认的Interactive Session。
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
sess = tf.InteractiveSession()

#定义初始化函数,以便重复使用创建权重、偏置、卷积层、池化层。
def weight_variable(shape):
 initial = tf.truncated_normal(shape, stddev=0.1)
 return tf.Variable(initial)

def bias_variable(shape):
 initial = tf.constant(0.1, shape=shape)
 return tf.Variable(initial)

def conv2d(x, W):
 return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
 return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
      strides=[1, 2, 2, 1], padding='SAME') 

#在设计卷积神经网络结构之前,定义输入的placeholder,x是特征,y_是真实Label。
#由于卷积神经网络会使用到空间结构信息,所以,需要将1D的输入向量转为2D图片结构,即从1*784的形式转换为原始的28*28结构。
#因为只有一个颜色通道,所以最终尺寸为[-1,28,28,1],其中‘-1'代表样本数量不固定,'1'代表颜色通道数量。
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
x_image = tf.reshape(x, [-1,28,28,1])
#定义第一个卷积层。
#先使用前面函数进行初始化,包括weights和bias。其中[5,5,1,32]代表卷积核尺寸为5**5,1个颜色通道,32个不同的卷积核。
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
#定义第二个卷积层。
#基本与第一个卷积层一样,只是其中的卷积核数量变成64.
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
#为了减轻过拟合,使用一个Dropout层,其用法是通过一个placeholder传入keep_prob比率来控制。
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
#定义损失函数cross_entropy,这里选择Adam优化器。
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv), reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
#继续定义评测准确率操作。
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

#开始训练过程。
tf.global_variables_initializer().run()
for i in range(20000):
 batch = mnist.train.next_batch(50)
 if i%100 == 0:
 train_accuracy = accuracy.eval(feed_dict={
  x:batch[0], y_: batch[1], keep_prob: 1.0})
 print("step %d, training accuracy %g"%(i, train_accuracy))
 train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
#全部训练完毕,在最终的测试集上进行全面测试,得到整体的分类准确率。
print("test accuracy %g"%accuracy.eval(feed_dict={
 x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

3、执行结果

Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
step 0, training accuracy 0.1
step 100, training accuracy 0.82
step 200, training accuracy 0.9
step 300, training accuracy 0.9
step 400, training accuracy 0.96
step 500, training accuracy 0.9
step 600, training accuracy 0.94
step 700, training accuracy 0.96
step 800, training accuracy 0.94
step 900, training accuracy 0.98
.  .  .  .  .

step 19600, training accuracy 1
step 19700, training accuracy 1
step 19800, training accuracy 1
step 19900, training accuracy 1
test accuracy 0.9929

4、模型分析

CNN模型的最终准确率约为99.2%,基本上可以满足对手写数字识别准确率的初步要求。相比于之前的MLP2%的错误率,CNN下降了60%左右。这里的性能提升主要在于更佳的网络设计,也就是卷积网络对图像特征的提取和抽象能力。依靠卷积核的权值共享,CNN的参数数量没有爆炸,降低计算量的同时也减去了过拟合,所以,整个模型的性能会有较大的提升。

四、TensorFlow实现进阶的卷积网络

1、基本介绍

这里使用CIFAR-10数据集,包含60,000张32*32的彩色图像,其中训练集50,000张,测试10,000张,一共标注为10类,分别为airplane、automobile、bird、cat、deer、dog、frog、horse、shhip、truck,,每一类图片6000张,其中没有任何重叠情况发生,例如:automobile 只包括小型汽车,truck只包括卡车,不会出现一张图片展现两类物体的现象。

2、实现过程

#载入数据
import cifar10,cifar10_input
import tensorflow as tf
import numpy as np
import time

max_steps = 3000 #训练轮数
batch_size = 128
data_dir = '/tmp/cifar10_data/cifar-10-batches-bin'#下载数据默认路径。

def variable_with_weight_loss(shape, stddev, wl):
 var = tf.Variable(tf.truncated_normal(shape, stddev=stddev))
 if wl is not None:
  weight_loss = tf.multiply(tf.nn.l2_loss(var), wl, name='weight_loss')
  tf.add_to_collection('losses', weight_loss)
 return var

#计算CNN的损失。
def loss(logits, labels):

 labels = tf.cast(labels, tf.int64)
 cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
  logits=logits, labels=labels, name='cross_entropy_per_example')
 cross_entropy_mean = tf.reduce_mean(cross_entropy, name='cross_entropy')
 tf.add_to_collection('losses', cross_entropy_mean)

 return tf.add_n(tf.get_collection('losses'), name='total_loss') 

cifar10.maybe_download_and_extract()

images_train, labels_train = cifar10_input.distorted_inputs(data_dir=data_dir,
               batch_size=batch_size)

images_test, labels_test = cifar10_input.inputs(eval_data=True,
            data_dir=data_dir,
            batch_size=batch_size)            

image_holder = tf.placeholder(tf.float32, [batch_size, 24, 24, 3])
label_holder = tf.placeholder(tf.int32, [batch_size])

#创建第一个卷积层。
weight1 = variable_with_weight_loss(shape=[5, 5, 3, 64], stddev=5e-2, wl=0.0)
kernel1 = tf.nn.conv2d(image_holder, weight1, [1, 1, 1, 1], padding='SAME')
bias1 = tf.Variable(tf.constant(0.0, shape=[64]))
conv1 = tf.nn.relu(tf.nn.bias_add(kernel1, bias1))
pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
      padding='SAME')
norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)

#创建第二个卷积层。
weight2 = variable_with_weight_loss(shape=[5, 5, 64, 64], stddev=5e-2, wl=0.0)
kernel2 = tf.nn.conv2d(norm1, weight2, [1, 1, 1, 1], padding='SAME')
bias2 = tf.Variable(tf.constant(0.1, shape=[64]))
conv2 = tf.nn.relu(tf.nn.bias_add(kernel2, bias2))
norm2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
pool2 = tf.nn.max_pool(norm2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
      padding='SAME')
#使用一个全连接层,先把第二个卷积层的输出结果flatten,将每个样本都变成一维向量。
reshape = tf.reshape(pool2, [batch_size, -1])
dim = reshape.get_shape()[1].value
weight3 = variable_with_weight_loss(shape=[dim, 384], stddev=0.04, wl=0.004)
bias3 = tf.Variable(tf.constant(0.1, shape=[384]))
local3 = tf.nn.relu(tf.matmul(reshape, weight3) + bias3)
#下面这个使用的全连接层,其隐含节点数下降了一半。
weight4 = variable_with_weight_loss(shape=[384, 192], stddev=0.04, wl=0.004)
bias4 = tf.Variable(tf.constant(0.1, shape=[192]))
local4 = tf.nn.relu(tf.matmul(local3, weight4) + bias4)

weight5 = variable_with_weight_loss(shape=[192, 10], stddev=1/192.0, wl=0.0)
bias5 = tf.Variable(tf.constant(0.0, shape=[10]))
logits = tf.add(tf.matmul(local4, weight5), bias5)
loss = loss(logits, label_holder)
train_op = tf.train.AdamOptimizer(1e-3).minimize(loss) #0.72
top_k_op = tf.nn.in_top_k(logits, label_holder, 1)
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()
tf.train.start_queue_runners()
#正式开始训练。
for step in range(max_steps):
 start_time = time.time()
 image_batch,label_batch = sess.run([images_train,labels_train])
 _, loss_value = sess.run([train_op, loss],feed_dict={image_holder: image_batch,
               label_holder:label_batch})
 duration = time.time() - start_time
 if step % 10 == 0:
  examples_per_sec = batch_size / duration
  sec_per_batch = float(duration)

  format_str = ('step %d, loss = %.2f (%.1f examples/sec; %.3f sec/batch)')
  print(format_str % (step, loss_value, examples_per_sec, sec_per_batch))

#评测模型在测试集上的准确率。
num_examples = 10000
import math
num_iter = int(math.ceil(num_examples / batch_size))
true_count = 0
total_sample_count = num_iter * batch_size
step = 0
while step < num_iter:
 image_batch,label_batch = sess.run([images_test,labels_test])
 predictions = sess.run([top_k_op],feed_dict={image_holder: image_batch,
             label_holder:label_batch})
 true_count += np.sum(predictions)
 step += 1
#最后,将准确率的评测结果计算并输出。
precision = true_count / total_sample_count
print('precision @ 1 = %.3f' % precision)

3、执行结果

由于笔者试了几次CIFAR-10模块,最后一步失败,所以没能正确显示,后续找机会再试试,但以笔者的初步判断,在CIFAR-10安装成功的情况下,执行结果应该是没问题的。感兴趣的朋友可以再看看,这里就不贴出相关结果了,望各位网友理解。

4、模型分析

在CIFAR-10数据集上,通过一个短时间小迭代次数的训练,可以达到约73%的准确率,后续若增加max_steps,期望准确率会逐渐增加。若max_steps比较大的化,建议使用学习速率衰减(decay)的SGD来进行训练,其训练过程中准确率的峰值会较高,约86%,因为这其中的L2正则和LRN层的使用均提升了模型准确率和模型的泛化性能。

五、小结

卷积网络最后的几个全连接层的作用是输出分类结果,前面的卷积层主要做特征提取工作,直到最后的全连接层才开始对特征进行组合分配,并进行分类。

卷积层一般需要和一个池化层连接,二者组合是做图像识别时的一个标准组件。卷积层的训练相对于全连接层更复杂,训练全连接层基本是进行一个些矩阵乘法运算,而目前卷积层的训练基本上依赖于cuDNN实现,其中的算法也相对复杂,甚至会涉及到傅里叶变换。

参考资料 主要参考资料《TensorFlow实战》(黄文坚 唐源 著)(电子工业出版社)。

以上这篇TensorFlow 实战之实现卷积神经网络的实例讲解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 利用TensorFlow训练简单的二分类神经网络模型的方法

    利用TensorFlow实现<神经网络与机器学习>一书中4.7模式分类练习 具体问题是将如下图所示双月牙数据集分类. 使用到的工具: python3.5    tensorflow1.2.1   numpy   matplotlib 1.产生双月环数据集 def produceData(r,w,d,num): r1 = r-w/2 r2 = r+w/2 #上半圆 theta1 = np.random.uniform(0, np.pi ,num) X_Col1 = np.random.unifo

  • TensorFlow实现iris数据集线性回归

    本文将遍历批量数据点并让TensorFlow更新斜率和y截距.这次将使用Scikit Learn的内建iris数据集.特别地,我们将用数据点(x值代表花瓣宽度,y值代表花瓣长度)找到最优直线.选择这两种特征是因为它们具有线性关系,在后续结果中将会看到.本文将使用L2正则损失函数. # 用TensorFlow实现线性回归算法 #---------------------------------- # # This function shows how to use TensorFlow to #

  • 用tensorflow构建线性回归模型的示例代码

    用tensorflow构建简单的线性回归模型是tensorflow的一个基础样例,但是原有的样例存在一些问题,我在实际调试的过程中做了一点自己的改进,并且有一些体会. 首先总结一下tf构建模型的总体套路 1.先定义模型的整体图结构,未知的部分,比如输入就用placeholder来代替. 2.再定义最后与目标的误差函数. 3.最后选择优化方法. 另外几个值得注意的地方是: 1.tensorflow构建模型第一步是先用代码搭建图模型,此时图模型是静止的,是不产生任何运算结果的,必须使用Session

  • Tensorflow使用支持向量机拟合线性回归

    支持向量机可以用来拟合线性回归. 相同的最大间隔(maximum margin)的概念应用到线性回归拟合.代替最大化分割两类目标是,最大化分割包含大部分的数据点(x,y).我们将用相同的iris数据集,展示用刚才的概念来进行花萼长度与花瓣宽度之间的线性拟合. 相关的损失函数类似于max(0,|yi-(Axi+b)|-ε).ε这里,是间隔宽度的一半,这意味着如果一个数据点在该区域,则损失等于0. # SVM Regression #---------------------------------

  • Tensorflow实现神经网络拟合线性回归

    本文实例为大家分享了Tensorflow实现神经网络拟合线性回归的具体代码,供大家参考,具体内容如下 一.利用简单的一层神经网络拟合一个函数 y = x^2 ,其中加入部分噪声作为偏置值防止拟合曲线过拟合 import tensorflow as tf import numpy as np import matplotlib.pyplot as plt # 生成-0.5到0.5间均匀发布的200个点,将数据变为二维,200行一列的数据 x_data = np.linspace(-0.5, 0.5

  • TensorFlow实现卷积神经网络CNN

    一.卷积神经网络CNN简介 卷积神经网络(ConvolutionalNeuralNetwork,CNN)最初是为解决图像识别等问题设计的,CNN现在的应用已经不限于图像和视频,也可用于时间序列信号,比如音频信号和文本数据等.CNN作为一个深度学习架构被提出的最初诉求是降低对图像数据预处理的要求,避免复杂的特征工程.在卷积神经网络中,第一个卷积层会直接接受图像像素级的输入,每一层卷积(滤波器)都会提取数据中最有效的特征,这种方法可以提取到图像中最基础的特征,而后再进行组合和抽象形成更高阶的特征,因

  • 使用tensorflow实现线性回归

    本文实例为大家分享了tensorflow实现线性回归的具体代码,供大家参考,具体内容如下 一.随机生成1000个点,分布在y=0.1x+0.3直线周围,并画出来 import tensorflow as tf import numpy as np import matplotlib.pyplot as plt num_points = 1000 vectors_set = [] for i in range(num_points): x1 = np.random.normal(0.0,0.55)

  • 运用TensorFlow进行简单实现线性回归、梯度下降示例

    线性回归属于监督学习,因此方法和监督学习应该是一样的,先给定一个训练集,根据这个训练集学习出一个线性函数,然后测试这个函数训练的好不好(即此函数是否足够拟合训练集数据),挑选出最好的函数(cost function最小)即可. 单变量线性回归: a) 因为是线性回归,所以学习到的函数为线性函数,即直线函数: b) 因为是单变量,因此只有一个x. 我们能够给出单变量线性回归的模型: 我们常称x为feature,h(x)为hypothesis. 上面介绍的方法中,我们肯定有一个疑问,怎样能够看出线性

  • 使用TensorFlow实现简单线性回归模型

    本文使用TensorFlow实现最简单的线性回归模型,供大家参考,具体内容如下 线性拟合y=2.7x+0.6,代码如下: import tensorflow as tf import numpy as np import matplotlib.pyplot as plt n = 201 # x点数 X = np.linspace(-1, 1, n)[:,np.newaxis] # 等差数列构建X,[:,np.newaxis]这个是shape,这一行构建了一个n维列向量([1,n]的矩阵) noi

  • TensorFlow实现RNN循环神经网络

    RNN(recurrent neural Network)循环神经网络 主要用于自然语言处理(nature language processing,NLP) RNN主要用途是处理和预测序列数据 RNN广泛的用于 语音识别.语言模型.机器翻译 RNN的来源就是为了刻画一个序列当前的输出与之前的信息影响后面节点的输出 RNN 是包含循环的网络,允许信息的持久化. RNN会记忆之前的信息,并利用之前的信息影响后面节点的输出. RNN的隐藏层之间的节点是有相连的,隐藏层的输入不仅仅包括输入层的输出,还包

随机推荐