当前位置:   article > 正文

用TensorFlow.NET搭建一个全连接神经网络

实验项目: 全联接网络模型 实验内容:搭建一个全联接神经网络模型,实现tensorflow

在本文中,我们将学习如何在C#中构建神经网络模型计算图。 与线性分类器相比,神经网络的关键优势在于它可以分离不可线性分离的数据。 我们将实现此模型来对MNIST数据集的手写数字图像进行分类。

我们要构建的神经网络的结构如下。

MNIST数据的手写数字图像有10个类(从0到9)。 该网络具有2个隐藏层:第一层具有200个隐藏单元(神经元),第二层具有10个神经元(称为分类器层)。

让我们一步一步地用代码来实现:

1. 准备数据

MNIST是手写数字的数据集,包含55,000个用于训练的示例,5,000个用于验证的示例和10,000个用于测试的示例。 这些数字已经过尺寸标准化,并且以固定尺寸的图像(28 x 28像素)为中心对齐,其值为0和1. 每个图像已被展开并转换为784个特征的矢量。 它也是深度学习数据集的基准测试数据。

我们先定义一些变量使以后可以更新它们。 值得注意的是,在线性模型中,我们必须将输入图像展开为矢量。

  1. using System;
  2. using NumSharp;
  3. using Tensorflow;
  4. using TensorFlowNET.Examples.Utility;
  5. using static Tensorflow.Python;
  6. const int img_h = 28;
  7. const int img_w = 28;
  8. int img_size_flat = img_h * img_w; // 784, the total number of pixels
  9. int n_classes = 10; // Number of classes, one class per digit

我们将编写自动加载MNIST数据的函数,并以我们想要的形状和格式返回它。 有一个MNIST数据助手可以让这件事更简单。

  1. Datasets mnist;
  2. public void PrepareData()
  3. {
  4. mnist = MnistDataSet.read_data_sets("mnist", one_hot: true);
  5. }

除了加载图像和相应标签的功能外,我们还需要两个功能:

randomize:随机化图像及其标签的顺序。 在每个Epoch的开始,我们将重新随机化数据样本的顺序,以确保训练的模型对数据的顺序不敏感。

  1. private (NDArray, NDArray) randomize(NDArray x, NDArray y)
  2. {
  3. var perm = np.random.permutation(y.shape[0]);
  4. np.random.shuffle(perm);
  5. return (mnist.train.images[perm], mnist.train.labels[perm]);
  6. }

get_next_batch:仅选择由batch_size变量确定的少量图像(根据Stochastic Gradient Descent方法)。

  1. private (NDArray, NDArray) get_next_batch(NDArray x, NDArray y, int start, int end)
  2. {
  3. var x_batch = x[$"{start}:{end}"];
  4. var y_batch = y[$"{start}:{end}"];
  5. return (x_batch, y_batch);
  6. }

2. 设置超参数

在训练集中有大约55,000个图像,使用所有图像来计算模型的梯度需要很长时间。 因此,我们通过随机梯度下降在优化器的每次迭代中使用一小批图像。

Epoch:所有训练样例的一次前向传递和一次向后传递。

批量大小:一次前进/后退传递中的训练样例数。 批量大小越大,您需要的内存空间就越大。

迭代:一组数据前向传递和后向传递图像的训练样例。

  1. int epochs = 10;
  2. int batch_size = 100;
  3. float learning_rate = 0.001f;
  4. int h1 = 200; // number of nodes in the 1st hidden layer

3. 构建神经网络

让我们先编写一些函数来帮助构建计算图。

变量:我们需要定义两个变量W和b来构造我们的线性模型。 我们使用适当大小和初始化的Tensorflow变量来定义它们。

  1. // weight_variable
  2. var in_dim = x.shape[1];
  3. var initer = tf.truncated_normal_initializer(stddev: 0.01f);
  4. var W = tf.get_variable("W_" + name,
  5. dtype: tf.float32,
  6. shape: (in_dim, num_units),
  7. initializer: initer);
  8. // bias_variable
  9. var initial = tf.constant(0f, num_units);
  10. var b = tf.get_variable("b_" + name,
  11. dtype: tf.float32,
  12. initializer: initial);

完全连接层:神经网络由完全连接(密集)层的堆栈组成。 具有权重(W)和偏差(b)变量,完全连接的层被定义为激活(W x X + b)。 完整的fc_layer函数如下:

  1. private Tensor fc_layer(Tensor x, int num_units, string name, bool use_relu = true)
  2. {
  3. var in_dim = x.shape[1];
  4. var initer = tf.truncated_normal_initializer(stddev: 0.01f);
  5. var W = tf.get_variable("W_" + name,
  6. dtype: tf.float32,
  7. shape: (in_dim, num_units),
  8. initializer: initer);
  9. var initial = tf.constant(0f, num_units);
  10. var b = tf.get_variable("b_" + name,
  11. dtype: tf.float32,
  12. initializer: initial);
  13. var layer = tf.matmul(x, W) + b;
  14. if (use_relu)
  15. layer = tf.nn.relu(layer);
  16. return layer;
  17. }

输入:现在我们需要定义适当的张量来输入我们的模型。 占位符变量是输入图像和相应标签的合适选择。 这允许我们将输入(图像和标签)更改为TensorFlow图。

  1. // Placeholders for inputs (x) and outputs(y)
  2. x = tf.placeholder(tf.float32, shape: (-1, img_size_flat), name: "X");
  3. y = tf.placeholder(tf.float32, shape: (-1, n_classes), name: "Y");

占位符`x`是为图像定义的,形状设置为`[None,img_size_flat]`,其中`None`表示张量可以保持任意数量的图像,每个图像是长度为`img_size_flat`的向量。

占位符`y`是与占位符变量x中输入的图像关联的真实标签的变量。 它包含任意数量的标签,每个标签是长度为`num_classes`的向量,为10。

网络层:在创建适当的输入后,我们必须将它传递给我们的模型。 由于我们有神经网络,我们可以使用fc_layer方法堆叠多个完全连接的层。 请注意,我们不会在最后一层使用任何激活函数(use_relu = false)。 原因是我们可以使用`tf.nn.softmax_cross_entropy_with_logits`来计算损失。

  1. // Create a fully-connected layer with h1 nodes as hidden layer
  2. var fc1 = fc_layer(x, h1, "FC1", use_relu: true);
  3. // Create a fully-connected layer with n_classes nodes as output layer
  4. var output_logits = fc_layer(fc1, n_classes, "OUT", use_relu: false);

损失函数:创建网络后,我们必须计算损失并对其进行优化,我们必须计算预测的准确率。

  1. // Define the loss function, optimizer, and accuracy
  2. var logits = tf.nn.softmax_cross_entropy_with_logits(labels: y, logits: output_logits);
  3. loss = tf.reduce_mean(logits, name: "loss");
  4. optimizer = tf.train.AdamOptimizer(learning_rate: learning_rate, name: "Adam-op").minimize(loss);
  5. var correct_prediction = tf.equal(tf.argmax(output_logits, 1), tf.argmax(y, 1), name: "correct_pred");
  6. accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32), name: "accuracy");

初始化变量:我们必须调用变量初始化程序操作来初始化所有变量。

var init = tf.global_variables_initializer();

完整的计算图如下所示:

4. 训练

创建图表后,我们就可以开始训练我们的模型。 为了训练模型,我们必须创建一个会话并在会话中运行计算图。

  1. // Number of training iterations in each epoch
  2. var num_tr_iter = mnist.train.labels.len / batch_size;
  3. with(tf.Session(), sess =>
  4. {
  5. sess.run(init);
  6. float loss_val = 100.0f;
  7. float accuracy_val = 0f;
  8. foreach (var epoch in range(epochs))
  9. {
  10. print($"Training epoch: {epoch + 1}");
  11. // Randomly shuffle the training data at the beginning of each epoch
  12. var (x_train, y_train) = randomize(mnist.train.images, mnist.train.labels);
  13. foreach (var iteration in range(num_tr_iter))
  14. {
  15. var start = iteration * batch_size;
  16. var end = (iteration + 1) * batch_size;
  17. var (x_batch, y_batch) = get_next_batch(x_train, y_train, start, end);
  18. // Run optimization op (backprop)
  19. sess.run(optimizer, new FeedItem(x, x_batch), new FeedItem(y, y_batch));
  20. if (iteration % display_freq == 0)
  21. {
  22. // Calculate and display the batch loss and accuracy
  23. var result = sess.run(new[] { loss, accuracy }, new FeedItem(x, x_batch), new FeedItem(y, y_batch));
  24. loss_val = result[0];
  25. accuracy_val = result[1];
  26. print($"iter {iteration.ToString("000")}: Loss={loss_val.ToString("0.0000")}, Training Accuracy={accuracy_val.ToString("P")}");
  27. }
  28. }
  29. // Run validation after every epoch
  30. var results1 = sess.run(new[] { loss, accuracy }, new FeedItem(x, mnist.validation.images), new FeedItem(y, mnist.validation.labels));
  31. loss_val = results1[0];
  32. accuracy_val = results1[1];
  33. print("---------------------------------------------------------");
  34. print($"Epoch: {epoch + 1}, validation loss: {loss_val.ToString("0.0000")}, validation accuracy: {accuracy_val.ToString("P")}");
  35. print("---------------------------------------------------------");
  36. }
  37. });

5. 测试

训练完成后,我们必须测试我们的模型,看看它在新数据集上的表现如何。

  1. var result = sess.run(new[] { loss, accuracy }, new FeedItem(x, mnist.test.images), new FeedItem(y, mnist.test.labels));
  2. loss_test = result[0];
  3. accuracy_test = result[1];
  4. print("---------------------------------------------------------");
  5. print($"Test loss: {loss_test.ToString("0.0000")}, test accuracy: {accuracy_test.ToString("P")}");
  6. print("---------------------------------------------------------");

从结果能看出,模型对于新的数据的识别能率在98%左右。

完整代码请参考:TensorFlow.NET 

如果您对.NET机器学习感兴趣,请关注我们的 新闻页

Github: https://github.com/SciSharp

 

 

转载于:https://my.oschina.net/haiping008/blog/3070926

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/779537
推荐阅读
相关标签
  

闽ICP备14008679号