[译] Scratch 平台的神经网络实现(R 语言)

2023-11-09

Scratch 平台的神经网络实现(R 语言)

这篇文章是针对那些有统计或者经济学背景的人们,帮助他们通过 R 语言上的 Scratch 平台更好地学习和理解机器学习知识。

Andrej Karpathy 在 CS231n 课程中这样说道

“我们有意识地在设计课程的时候,于反向传播算法的编程作业中包含了对最底层的数据的计算要求。学生们需要在原始的 numpy 库中使数据在各层中正向、反向传播。一些学生因而难免在课程的留言板上抱怨(这些复杂的计算)”

如果框架已经为你完成了反向传播算法(BP 算法)的计算,你又何苦折磨自己而不去探寻更多有趣的深度学习问题呢?

import keras
model = Sequential()
model.add(Dense(512, activation=’relu’, input_shape=(784,)))
model.add(Dense(10, activation=’softmax’))
model.compile(loss=’categorical_crossentropy’, optimizer=RMSprop())
model.fit()复制代码

Karpathy教授,将“智力上的好奇”或者“你可能想要晚些提升核心算法”的论点抽象出来,认为计算实际上是一种泄漏抽象(译者注:“抽象泄漏”是软件开发时,本应隐藏实现细节的抽象化不可避免地暴露出底层细节与局限性。抽象泄露是棘手的问题,因为抽象化本来目的就是向用户隐藏不必要公开的细节--维基百科):

“人们很容易陷入这样的误区中-认为你可以简单地将任意的神经层组合在一起然后反向传播算法会‘令它们自己在你的数据上工作起来’。”

因此,我写这篇文章的目的有两层:

  1. 理解神经网络背后的抽象泄漏(通过在 Scratch 平台上操作),而这些东西的重要性恰恰是我开始所忽略的。这样如果我的模型没有达到预期的学习效果,我可以更好地解决问题,而不是盲目地改变优化方案(甚至更换学习框架)。

  2. 一个深度神经网络(DNN),一旦被拆分成块,对于 AI 领域之外的人们也再也不是一个黑箱了。相反,对于大多数有基本的统计背景的人来说,是一个个非常熟悉的话题的组合。我相信他们只需要学习很少的一些(只是那些如何将这一块块知识组合一起)知识就可以在一个全新的领域获得不错的洞察力。

从线性回归开始,借着 R-notebook,通过解决一系列的数学和编程问题直至了解深度神经网络(DNN)。希望能够借此展示出来,你所需学习的新知识其实只有很少的一部分。

笔记

github.com/ilkarman/De…
github.com/ilkarman/De…
github.com/ilkarman/De…
github.com/ilkarman/De…

一、线性回归(见笔记(github-ipynb)

在 R 中解决最小二乘法的计算器的闭包解决方案只需如下几行:

# Matrix of explanatory variables
X <- as.matrix(X)
# Add column of 1s for intercept coefficient
intcpt <- rep(1, length(y))
# Combine predictors with intercept
X <- cbind(intcpt, X)
# OLS (closed-form solution)
beta_hat <- solve(t(X) %*% X) %*% t(X) %*% y复制代码

变量 beta_hat 所形成的向量包含的数值,定义了我们的“机器学习模型”。线性回归是用来预测一个连续的变量的(例如:这架飞机会延误多久)。在预测分类的时候(例如:这架飞机会延误吗-会/不会),我们希望我们的预测能够落在0到1之间,这样我们可以将其转换为各个种类的事件发生的可能性(根据所给的数据)。

当我们只有两个互斥的结果时我们将使用一个二项逻辑回归。当候选结果(或者分类)多于两个时,即多项互斥(例如:这架飞机延误时间可能在5分钟内、5-10分钟或多于10分钟),我们将使用多项逻辑回归(或者“Softmax 回归”)(译者注:Softmax 函数是逻辑函数的一种推广,更多知识见知乎)。在这种情况下许多类别不是互斥的(例如:这篇文章中的“R”,“神经网络”和“统计学”),我们可以采用二项式逻辑回归(译者注:不是二项逻辑回归)。

另外,我们也可以用梯度下降(GD)这种迭代法来替代我们上文提到的闭包方法。整个过程如下:

  • 从随机地猜测权重开始
  • 将所猜测的权重值代入损失函数中
  • 将猜测值移向梯度的相反方向移动一小步(即我们所谓的“学习频率”)
  • 重复上述步骤 N 次

GD 仅仅使用了 Jacobian 矩阵 (而不是 Hessian 矩阵),不过我们知道, 当我们的损失函数为凸函数时,所有的极小值即(局部最小值)为(全局)最小值,因此 GD 总能够收敛至全局最小值。

线性回归中所用的损失函数是均方误差函数:

要使用 GD 方法我们只需要找出 beta_hat 的偏导数(即 'delta'/梯度)

在 R 中实现方法如下:

# Start with a random guess
beta_hat <- matrix(0.1, nrow=ncol(X_mat))
  # Repeat below for N-iterations
  for (j in 1:N)
  {
    # Calculate the cost/error (y_guess - y_truth)
    residual <- (X_mat %*% beta_hat) - y
    # Calculate the gradient at that point
    delta <- (t(X_mat) %*% residual) * (1/nrow(X_mat))
    # Move guess in opposite direction of gradient
    beta_hat <- beta_hat - (lr*delta)
  }复制代码

200次的迭代之后我们会得到和闭包方法一样的梯度与参数。除了这代表着我们的进步意外(我们使用了 GD),这个迭代方法在当闭包方法因矩阵过大,而无法计算矩阵的逆的时候,也非常有用(因为有内存的限制)。

第二步 - 逻辑回归 (见笔记(github-ipynb))

逻辑回归即一种用来解决二项分类的线性回归方法。它与标准的线性回归主要的两种不同在于:

  1. 我们使用一种称为 logistic-sigmoid 的 ‘激活’/链接函数来将输出压缩至 0 到 1 的范围内
  2. 不是最小化损失的方差而是最小化伯努利分布的负对数似然

其它的都保持不变。

我们可以像这样计算我们的激活函数:

sigmoid <- function(z){1.0/(1.0+exp(-z))}复制代码

我们可以在 R 中这样创建对数似然函数:

log_likelihood <- function(X_mat, y, beta_hat)
{
  scores <- X_mat %*% beta_hat
  ll <- (y * scores) - log(1+exp(scores))
  sum(ll)
}复制代码

这个损失函数(逻辑损失或对数损失函数)也叫做交叉熵损失。交叉熵损失根本上来讲是对“意外”的一种测量,并且会成为所有接下来的模型的基础,所以值得多花一些时间。

如果我们还像以前一样建立最小平方损失函数,由于我们目前拥有的是一个非线性激活函数(sigmoid),那么损失函数将因不再是凸函数而使优化变得困难。

我们可以为两个分类设立自己的损失函数。当 y=1 时,我们希望我们的损失函数值在预测值接近0的时候变得非常高,在接近1的时候变得非常低。当 y=0 时,我们所期望的与之前恰恰相反。这导致了我们有了如下的损失函数:

这里的损失函数中的 delta 与我们之前的线性回归中的 delta 非常相似。唯一的不同在于我们在这里将 sigmoid 函数也应用在了预测之中。这意味着逻辑回归中的梯度下降函数也会看起来很相似:

logistic_reg <- function(X, y, epochs, lr)
{
  X_mat <- cbind(1, X)
  beta_hat <- matrix(1, nrow=ncol(X_mat))
  for (j in 1:epochs)
  {
    # For a linear regression this was:
    # 1*(X_mat %*% beta_hat) - y
    residual <- sigmoid(X_mat %*% beta_hat) - y
    # Update weights with gradient descent
    delta <- t(X_mat) %*% as.matrix(residual, ncol=nrow(X_mat)) *  (1/nrow(X_mat))
    beta_hat <- beta_hat - (lr*delta)
  }
  # Print log-likliehood
  print(log_likelihood(X_mat, y, beta_hat))
  # Return
  beta_hat
}复制代码

三、Softmax 回归函数(无笔记)

逻辑回归的推广即为多项逻辑回归(也称为 ‘softmax 函数’),是对两项以上的分类进行预测的。我尚未在 R 中建立这个例子,因为下一步的神经网络中也有一些东西简化之后与之相似,然而为了完整起见,如果你仍然想要创建它的话,我还是要强调一下这里主要的不同。

首先,我们不再用 sigmoid 函数来讲我们所得的值压缩在 0 至 1 之间:

我们用 softmax 函数来将 n 个值的和压缩至 1:

这样意味着每个类别所得的值,可以根据所给的条件,被转化为该类的概率。同时也意味着当我们希望提高某一分类的权重来提高它所获得的概率的时候,其它分类的出现概率会有所下降。也就是说,我们的各个类别是互斥的。

其次,我们使用一个更加通用的交叉熵损失函数:

要想知道为什么-记住对于二项分类(如之前的例子)我们有两个类别:j = 2,在每个类别是互斥的,a1 + a2 = 1 且 y 是一位有效编码(one-hot)所以 y1+y2=1,我们可以将通用公式重写为:
(译者注:one-hot是将分类的特征转化为更加适合分类和回归算法的数据格式(Quora-Håkon Hapnes Strand),中文资料可见此

这与我们刚开始的等式是相同的。然而,我们现在将 j=2 的条件放宽。这里的交叉熵损失函数可以被看出来有着与二项分类的逻辑输出的交叉熵有着相同的梯度。

然而,即使梯度有着相同的公式,也会因为激活函数代入了不同的值而不一样(用了 softmax 而不是逻辑中的 sigmoid)。

在大多数的深度学习框架中,你可以选择‘二项交叉熵(binary_crossentropy)’或者‘分类交叉熵(categorical_crossentropy)’损失函数。这取决于你的最后一层神经包含的是 sigmoid 还是 softmax 激活函数,相对应着,你可以选择‘二项交叉熵(binary_crossentropy)’或者‘分类交叉熵(categorical_crossentropy)’。而由于梯度相同,神经网络的训练并不会被影响,然而所得到的损失(或评测值)会由于搞混它们而错误。

之所以要涉及到 softmax 是因为大多数的神经网络,会在各个类别互斥的时候,用 softmax 层作为最后一层(读出层),用多项交叉熵(也叫分类交叉熵)损失函数,而不是用 sigmoid 函数搭配二项交叉熵损失函数。尽管多项 sigmoid 也可以用于多类别分类(并且会被用于下个例子中),但这总体上仅用于多项不互斥的时候。有了 softmax 作为输出,由于输出的和被限制为 1,我们可以直接将输出转化为概率。

四、神经网络(见笔记(github-ipynb))

一个神经网络可以被看作为一系列的逻辑回归堆叠在一起。这意味着我们可以说,一个逻辑回归实际上是一个(带有 sigmoid 激活函数)无隐藏层的神经网络。

隐藏层,使神经网络具有非线性且导致了用于通用近似定理所描述的特性。该定理声明,一个神经网络和一个隐藏层可以逼近任何线性或非线性的函数。而隐藏层的数量可以扩展至上百层。

如果将神经网络看作两个东西的结合会很有用:1)很多的逻辑回归堆叠在一起形成‘特征生成器’ 2)一个 softmax 回归函数构成的单个读出层。近来深度学习的成功可归功于‘特征生成器’。例如:在以前的计算机视觉领域,我们需要痛苦地声明我们需要找到各种长方形,圆形,颜色和结合方式(与经济学家们如何决定哪些相互作用需要用于线性回归中相似)。现在,隐藏层是对决定哪个特征(哪个‘相互作用’)需要提取的优化器。很多的深度学习实际上是通过用一个训练好的模型,去掉读出层,然后用那些特征作为输入(或者是促进决策树(boosted decision-trees))来生成的。

隐藏层同时也意味着我们的损失函数在参数中不是一个凸函数,我们不能够通过一个平滑的山坡来到达底部。我们会用随机梯度下降(SGD)而不是梯度下降(GD),不像我们之前在逻辑回归中做的一样,这样基本上在每一次小批量(mini-batch)(比观察总数小很多)被在神经网络中传播后都会重编观察(随机)并更新梯度。这里有很多 SGD 的替代方法,Sebastian Ruder 为我们做了很多工作。我认为这确实是个迷人的话题,不过却超出这篇博文所讨论的范围了,很遗憾。简要来讲,大多数优化方法是一阶的(包括 SGD,Adam,RMSprop和 Adagrad)因为计算二阶函数的计算难度过高。然而,一些一阶方法有一个固定的学习频率(SGD)而有一些拥有适应性学习频率(Adam),这意味着我们通过成为损失函数所更新权重的‘数量’-将会在开始有巨大的变化而随着我们接近目标而逐渐变小。

需要弄清楚的一点是,最小化训练数据上的损失并非我们的主要目标-理论上我们希望最小化‘不可见的’(测试)数据的损失;因此所有的优化方法都代表着已经一种假设之下,即训练数据的的低损失会以同样的(损失)分布推广至‘新’的数据。这意味着我们可能更青睐于一个有着更高的训练数据损失的神经网络;因为它在验证数据上的损失很低(即那些未曾被用于训练的数据)-我们则会说该神经网络在这种情况下‘过度拟合’了。这里有一些近期的论文声称,他们发现了很多很尖的最小值点,所以适应性优化方法并不像 SGD 一样能够很好的推广。(译者注:即算法在一些验证数据中表现地出奇的差)

之前我们需要将梯度反向传播一层,现在一样,我们也需要将其反向传播过所有的隐藏层。关于反向传播算法的解释,已经超出了本文的范围,然而理解这个算法却是十分必要的。这里有一些不错的资源可能对各位有所帮助。

我们现在可以在 Scratch 平台上用 R 通过四个函数建立一个神经网络了。

  1. 我们首先初始化权重:

    neuralnetwork <- function(sizes, training_data, epochs, mini_batch_size, lr, C, verbose=FALSE, validation_data=training_data)

由于我们将参数进行了复杂的结合,我们不能简单地像以前一样将它们初始化为 1 或 0,神经网络会因此而在计算过程中卡住。为了防止这种情况,我们采用高斯分布(不过就像那些优化方法一样,这也有许多其他的方法):

biases <- lapply(seq_along(listb), function(idx){
    r <- listb[[idx]]
    matrix(rnorm(n=r), nrow=r, ncol=1)
    })

weights <- lapply(seq_along(listb), function(idx){
    c <- listw[[idx]]
    r <- listb[[idx]]
    matrix(rnorm(n=r*c), nrow=r, ncol=c)
    })复制代码
  1. 我们使用随机梯度下降(SGD)作为我们的优化方法:
    SGD <- function(training_data, epochs, mini_batch_size, lr, C, sizes, num_layers, biases, weights,verbose=FALSE, validation_data)
    {
      # Every epoch
      for (j in 1:epochs){
    # Stochastic mini-batch (shuffle data)
    training_data <- sample(training_data)
    # Partition set into mini-batches
    mini_batches <- split(training_data,
      ceiling(seq_along(training_data)/mini_batch_size))
    # Feed forward (and back) all mini-batches
    for (k in 1:length(mini_batches)) {
      # Update biases and weights
      res <- update_mini_batch(mini_batches[[k]], lr, C, sizes, num_layers, biases, weights)
      biases <- res[[1]]
      weights <- res[[-1]]
    }
      }
      # Return trained biases and weights
      list(biases, weights)
    }复制代码
  1. 作为 SGD 方法的一部分,我们更新了

     update_mini_batch <- function(mini_batch, lr, C, sizes, num_layers, biases, weights)
     {
       nmb <- length(mini_batch)
       listw <- sizes[1:length(sizes)-1]
       listb <-  sizes[-1]
    
     # Initialise updates with zero vectors (for EACH mini-batch)
       nabla_b <- lapply(seq_along(listb), function(idx){
     r <- listb[[idx]]
     matrix(0, nrow=r, ncol=1)
       })
       nabla_w <- lapply(seq_along(listb), function(idx){
     c <- listw[[idx]]
     r <- listb[[idx]]
     matrix(0, nrow=r, ncol=c)
       })
    
     # Go through mini_batch
       for (i in 1:nmb){
     x <- mini_batch[[i]][[1]]
     y <- mini_batch[[i]][[-1]]
     # Back propagation will return delta
     # Backprop for each observation in mini-batch
     delta_nablas <- backprop(x, y, C, sizes, num_layers, biases, weights)
     delta_nabla_b <- delta_nablas[[1]]
     delta_nabla_w <- delta_nablas[[-1]]
     # Add on deltas to nabla
     nabla_b <- lapply(seq_along(biases),function(j)
       unlist(nabla_b[[j]])+unlist(delta_nabla_b[[j]]))
     nabla_w <- lapply(seq_along(weights),function(j)
       unlist(nabla_w[[j]])+unlist(delta_nabla_w[[j]]))
       }
       # After mini-batch has finished update biases and weights:
       # i.e. weights = weights - (learning-rate/numbr in batch)*nabla_weights
       # Opposite direction of gradient
       weights <- lapply(seq_along(weights), function(j)
     unlist(weights[[j]])-(lr/nmb)*unlist(nabla_w[[j]]))
       biases <- lapply(seq_along(biases), function(j)
     unlist(biases[[j]])-(lr/nmb)*unlist(nabla_b[[j]]))
       # Return
       list(biases, weights)
     }复制代码
  2. 我们用来计算 delta 的算法是反向传播算法。

在这个例子中我们使用交叉熵损失函数,产生了以下的梯度:

cost_delta <- function(method, z, a, y) {if (method=='ce'){return (a-y)}}复制代码

同时,为了与我们的逻辑回归例子保持连续,我们在隐藏层和读出层上使用 sigmoid 激活函数:

# Calculate activation function
    sigmoid <- function(z){1.0/(1.0+exp(-z))}
    # Partial derivative of activation function
    sigmoid_prime <- function(z){sigmoid(z)*(1-sigmoid(z))}复制代码

如之前所说,一般来讲 softmax 激活函数适用于读出层。对于隐藏层,线性整流函数(ReLU)更加地普遍,这里就是最大值函数(负数被看作为0)。隐藏层使用的激活函数可以被想象为一场扛着火焰同时保持它(梯度)不灭的比赛。sigmoid 函数在0和1处平坦化,成为一个平坦的梯度,相当于火焰的熄灭(我们失去了信号)。而线性整流函数(ReLU)帮助保存了这个梯度。

反向传播函数被定义为:

backprop <- function(x, y, C, sizes, num_layers, biases, weights)复制代码

请在笔记中查看完整的代码-然而原则还是一样的:我们有一个正向传播,使得我们在网络中将权重传导过所有神经层,并产生预测值。然后将预测值代入损失梯度函数中并将所有神经层中的权重更新。

这总结了神经网络的建成(搭配上你所需要的尽可能多的隐藏层)。将隐藏层的激活函数换为 ReLU
函数,读出层换为 softmax 函数,并且加上 L1 和 L2 的归一化,是一个不错的练习。把它在笔记中的 iris 数据集跑一遍,只用一个隐藏层,包含40个神经元,我们就可以在大概30多回合训练后得到一个96%精确度的神经网络。

笔记中还提供了一个100个神经元的手写识别系统的例子,来根据28*28像素的图像预测数字。

五、卷积神经网络([见笔记(github.com/ilkarman/De…)]

在这里,我们只会简单地测试卷积神经网络(CNN)中的正向传播。CNN 首次受到关注是因为1998年的LeCun的精品论文。自此之后,CNN 被证实是在图像、声音、视频甚至文字中最好的算法。

图像识别开始时是一个手动的过程,研究者们需要明确图像的哪些比特(特征)对于识别有用。例如,如果我们希望将一张图片归类进‘猫’或‘篮球’,我们可以写一些代码提取出颜色(如篮球是棕色)和形状(猫有着三角形耳朵)。这样我们或许就可以在这些特征上跑一个线性回归,来得到三角形个数和图像是猫还是树的关系。这个方法很受图片的大小、角度、质量和光线的影响,有很多问题。规模不变的特征变换(SIFT) 在此基础上做了大幅提升并曾被用来对一个物体提供‘特征描述’,这样可以被用来训练线性回归(或其他的关系型学习器)。然而,这个方法有个一成不变的规则使其不能被为特定的领域而优化。

CNN 卷积神经网络用一种很有趣的方式看待图像(提取特征)。开始时,他们只观察图像的很小一部分(每次),比如说一个大小为 5*5 像素的框(一个过滤器)。2D 用于图像的卷积,是将这个框扫遍整个图像。这个阶段会专门用于提取颜色和线段。然而,下一个神经层会转而关注之前过滤器的结合,因而‘放大来观察’。在一定数量的层数之后,神经网络会放的足够大而能识别出形状和更大的结构。

这些过滤器最终会成为神经网络需要去学习、识别的‘特征’。接着,它就可以通过统计各个特征的数量来识别其与图像标签(如‘篮球’或‘猫’)的关系。这个方法看起来对图片来讲很自然-因为它们可以被拆成小块来描述(它们的颜色,纹理等)。CNN 看起来在图像分形特征分析方面会蓬勃发展。这也意味着它们不一定适合其他形式的数据,如 excel 工作单中就没有固有的样式:我们可以改变任意几列的顺序而数据还是一样的——不过在图像中交换像素点的位置就会导致图像的改变。

在之前的例子中我们观察的是一个标准的神经网络对手写字体的归类。在神经网络中的 i 层的每个神经元,与 j 层的每个神经元相连-我们所框中的是整个图像(译者注:与 CNN 之前的 5*5 像素的框不同)。这意味着如果我们学习了数字 2 的样子,我们可能无法在它被错误地颠倒的时候识别出来,因为我们只见过它正的样子。CNN 在观察数字 2 的小的比特时并且在比较样式的时候有很大的优势。这意味着很多被提取出的特征对各种旋转,歪斜等是免疫的(译者注:即适用于所有变形)。对于更多的细节,Brandon 在这里解释了什么是真正的 CNN。

我们在 R 中如此定义 2D 卷积函数:

convolution <- function(input_img, filter, show=TRUE, out=FALSE)
{
  conv_out <- outer(
    1:(nrow(input_img)-kernel_size[[1]]+1),
    1:(ncol(input_img)-kernel_size[[2]]+1),
    Vectorize(function(r,c) sum(input_img[r:(r+kernel_size[[1]]-1),
                                          c:(c+kernel_size[[2]]-1)]*filter))
  )
}复制代码

并用它对一个图片应用了一个 3*3 的过滤器:

conv_emboss <- matrix(c(2,0,0,0,-1,0,0,0,-1), nrow = 3)
convolution(input_img = r_img, filter = conv_emboss)复制代码

你可以查看笔记来看结果,然而这看起来是从图片中提取线段。否则,卷积可以‘锐化’一张图片,就像一个3*3的过滤器:

conv_sharpen <- matrix(c(0,-1,0,-1,5,-1,0,-1,0), nrow = 3)
convolution(input_img = r_img, filter = conv_sharpen)复制代码

很显然我们可以随机地随机地初始化一些个数的过滤器(如:64个):

filter_map <- lapply(X=c(1:64), FUN=function(x){
    # Random matrix of 0, 1, -1
    conv_rand <- matrix(sample.int(3, size=9, replace = TRUE), ncol=3)-2
    convolution(input_img = r_img, filter = conv_rand, show=FALSE, out=TRUE)
})复制代码

我们可以用以下的函数可视化这个 map:

square_stack_lst_of_matricies <- function(lst)
{
    sqr_size <- sqrt(length(lst))
    # Stack vertically
    cols <- do.call(cbind, lst)
    # Split to another dim
    dim(cols) <- c(dim(filter_map[[1]])[[1]],
                   dim(filter_map[[1]])[[1]]*sqr_size,
                   sqr_size)
    # Stack horizontally
    do.call(rbind, lapply(1:dim(cols)[3], function(i) cols[, , i]))
}复制代码

在运行这个函数的时候我们意识到了整个过程是如何地高密度计算(与标准的全连接神经层相比)。如果这些 feature-map 不是那些那么有用的集合(也就是说,很难在此时降低损失)然后反向传播会意味着我们将会得到不同的权重,与不同的 feature-map 相关联,对于进行的聚类很有帮助。

很明显的我们将卷积建立在其他的卷积中(而且因此需要一个深度网络)所以线段构成了形状而形状构成了鼻子,鼻子构成了脸。测试一些训练的网络中的feature map来看看神经网络实际学到了什么也是一件有趣的事。

References

neuralnetworksanddeeplearning.com/

www.youtube.com/user/Brando…

colah.github.io/posts/2014-…

houxianxu.github.io/2015/04/23/…

www.ics.uci.edu/~pjsadows/n…


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[译] Scratch 平台的神经网络实现(R 语言) 的相关文章

随机推荐

  • EditText输入内容拦截和监听删除

    系列文章目录 文章目录 系列文章目录 前言 拦截输入内容提交 监听软件盘删除按钮点击事件 监听输入框文字粘贴 复制 全选等 code 前言 有时候我们会有一些特殊的需求 需要对输入框进行特殊的处理 比如 对输入内容去除特殊字符操作 或拦截输
  • statsmodels.tsa.stattools.adfuller 的用法

    statsmodels tsa stattools adfuller x maxlag None regression c autolag AIC store False regresults False source 增广Dickey F
  • Linux下vi命令编辑器,编辑 ,保存和退出

    1 vi 文件名 vi后面有空格 接着按回车即可打开对应的文件 如果没有对应的文件 那么vi命令就会自动创建一个新的 2 vi打开文件后是命令模式状态 要用i或者a命令或Insert键才可进入可编辑的状态 最下面会出现 INSERT 3 保
  • python 列表操作方法详解及例子

    原文链接 https www cnblogs com wj 1314 p 8433116 html 列表是Python中最基本的数据结构 列表是最常用的Python数据类型 列表是一个数据的集合 集合内可以放任何数据类型 可对集合方便的增删
  • 云服务器安装操作系统后如何连接,服务器如何安装操作系统

    服务器如何安装操作系统 内容精选 换一换 如果您需要使用毕昇编译器 则需要先在服务端安装毕昇编译器 毕昇编译器基于开源LLVM开发 并进行了优化和改进 同时将flang作为默认的Fortran语言前端编译器 是针对鲲鹏平台的高性能编译器 当
  • 华为OD机考-模拟消息队列(C,python)

    题目描述 让我们来模拟一个消息队列的运作 有一个发布者和若干消费者 发布者会在给定的时刻向消息队列发送消息 若此时消息队列有消费者订阅 这个消息会被发送到订阅的消费者中优先级最高 输入中消费者按优先级升序排列 的一个 若此时没有订阅的消费者
  • urldecode 报错 Malformed UTF-8 characters, possibly incorrectly encoded

    使用urlencode 编码了一段字符串写入数据库 读取的时候使用urldecode 解码报错 Malformed UTF 8 characters possibly incorrectly encoded 解决方案 检查一下是否保存到数据
  • ajax不弹出新页面问题

    ajax默认是异步请求 做局部刷新的 指的是当前页数据渲染的 如果后台是转发或者重定向了 如果用ajax的话是不会弹出新的页面的 from提交的话 如果后台是转发或者重定向了 是可以打开新的页面的
  • 【人脸识别】【python】Object arrays cannot be loaded when allow_pickle=False解决方案

    2020年2月11日 0次阅读 共1625个字 0条评论 0人点赞 QueenDekimZ mtcnn debug 用mtcnn对LFW人脸数据集进行人脸检测与关键点对齐 并裁剪到160 160维 为后续facenet训练作training
  • wx.login wx.getUserProfile 获取登录凭证

    wx login 调用接口获取登录凭证 code 通过凭证进而换取用户登录态信息 包括用户在当前小程序的唯一标识 openid 微信开放平台帐号下的唯一标识 unionid 若当前小程序已绑定到微信开放平台帐号 及本次登录的会话密钥 ses
  • 通过hexo快速搭建个人博客

    个人博客预览点击这里 菜卷的博客 快速搭建一个博客 一 需要安装的工具 二 开始安装Hexo 三 安装完成后 初始化项目 四 在项目根目录下执行命令 五 启动项目 六 部署到github 七 配置文件 八 安装next主题 九 优化next
  • C语言程序实训--实验设备管理系统

    之前学校c语言程序实训课要求写的 如果程序有错误或可以改进的地方 希望各位指出 开发环境 IDE Visual Studio Code Dev C 处理器 AMD Ryzen 7 PRO 6850HS with Radeon Graphic
  • 73家!华为鸿蒙OS合作伙伴汇总

    6月2日 华为发布了最新版的鸿蒙操作系统 HarmonyOS 2 0 以及一系列搭载鸿蒙的硬件产品 比如手机 手表 平板 耳机 显示器等等 如今的智能终端越来越多 厂商不可能为每个设备单独准备一个系统 因为这不仅让开发者工作量倍增 消费者用
  • Flask网站中使用Keras时报错“Tensor Tensor(*) is not an element of this graph”

    HyperLPR车牌识别程序本地中能进行正常识别 但将其放到flask搭建的网站中进行识别 不能运行 并报错 Tensor Tensor is not an element of this graph HyperLPR中的识别模型采用的是K
  • Mask掩码

    Python中Mask的用法 引例 Numpy的MaskedArray模块 小于 或小于等于 给定数值 大于 或大于等于 给定数值 在给定范围内 超出给定范围 在算术运算期间忽略NaN和 或infinite值 All men are scu
  • Count Color

    http poj org problem id 2777 Description Chosen Problem Solving and Program design as an optional course you are require
  • 【QT】——布局

    目录 1 在UI窗口中布局 2 API设置布局 2 1 QLayout 2 2 QHBoxLayout 2 3 QVBoxLayout 2 4 QGirdLayout 注意 示例 Qt 窗口布局是指将多个子窗口按照某种排列方式将其全部展示到
  • Apifox—诠释国产接口管理工具新高度

    揭开Apifox的神秘面纱 曾经在对于接口管理和调试工作上 大量的开发者往往会选择使用Swagger做接口文档管理 用Postman做接口调试工具 然而这样使用的痛处其实也不言而喻 原本同一类型的工作却被放置在不同的软件工具上 并且对于接口
  • 图像二值化方法--OTSU(最大类间方差法)

    前面学习了直方图双峰法 图像二值化方法中的阈值法 最大类间方差法 OTSU 是找到自适应阈值的常用方法 原理参考了冈萨雷斯的 数字图像处理 以下是自己写的函数 获取灰度图in的OTSU阈值 int Segment otsuMat Mat i
  • [译] Scratch 平台的神经网络实现(R 语言)

    原文地址 Neural Networks from Scratch in R 原文作者 Ilia Karmanov 译文出自 掘金翻译计划 本文永久链接 github com xitu gold m 译者 CACppuccino 校对者 I