# 机器学习笔记(二)——手动编写神经网络前向反向传播

手动编写神经网络前向/反向传递

前向传递

\qquad前向传递的原理非常容易,如下图所示。

# 机器学习笔记(二)——手动编写神经网络前向反向传播

a 1 ( 2 ) = g ( Θ 10 ( 1 ) x 0 + Θ 11 ( 1 ) x 1 + Θ 12 ( 1 ) x 2 + Θ 13 ( 1 ) x 3 ) a 2 ( 2 ) = g ( Θ 20 ( 1 ) x 0 + Θ 21 ( 1 ) x 1 + Θ 22 ( 1 ) x 2 + Θ 23 ( 1 ) x 3 ) a 3 ( 2 ) = g ( Θ 30 ( 1 ) x 0 + Θ 31 ( 1 ) x 1 + Θ 32 ( 1 ) x 2 + Θ 33 ( 1 ) x 3 ) h Θ ( x ) = g ( Θ 10 ( 2 ) a 0 ( 2 ) + Θ 11 ( 2 ) a 1 ( 2 ) + Θ 12 ( 2 ) a 2 ( 2 ) + Θ 13 ( 2 ) a 3 ( 2 ) ) (1) \begin{aligned} &a_{1}^{(2)}=g\left(\Theta_{10}^{(1)} x_{0}+\Theta_{11}^{(1)} x_{1}+\Theta_{12}^{(1)} x_{2}+\Theta_{13}^{(1)} x_{3}\right) \ &a_{2}^{(2)}=g\left(\Theta_{20}^{(1)} x_{0}+\Theta_{21}^{(1)} x_{1}+\Theta_{22}^{(1)} x_{2}+\Theta_{23}^{(1)} x_{3}\right) \ &a_{3}^{(2)}=g\left(\Theta_{30}^{(1)} x_{0}+\Theta_{31}^{(1)} x_{1}+\Theta_{32}^{(1)} x_{2}+\Theta_{33}^{(1)} x_{3}\right) \ &h_{\Theta}(x)=g\left(\Theta_{10}^{(2)} a_{0}^{(2)}+\Theta_{11}^{(2)} a_{1}^{(2)}+\Theta_{12}^{(2)} a_{2}^{(2)}+\Theta_{13}^{(2)} a_{3}^{(2)}\right) \end{aligned} \tag{1}​a 1 (2 )​=g (Θ1 0 (1 )​x 0 ​+Θ1 1 (1 )​x 1 ​+Θ1 2 (1 )​x 2 ​+Θ1 3 (1 )​x 3 ​)a 2 (2 )​=g (Θ2 0 (1 )​x 0 ​+Θ2 1 (1 )​x 1 ​+Θ2 2 (1 )​x 2 ​+Θ2 3 (1 )​x 3 ​)a 3 (2 )​=g (Θ3 0 (1 )​x 0 ​+Θ3 1 (1 )​x 1 ​+Θ3 2 (1 )​x 2 ​+Θ3 3 (1 )​x 3 ​)h Θ​(x )=g (Θ1 0 (2 )​a 0 (2 )​+Θ1 1 (2 )​a 1 (2 )​+Θ1 2 (2 )​a 2 (2 )​+Θ1 3 (2 )​a 3 (2 )​)​(1 )

; 反向传递

# 机器学习笔记(二)——手动编写神经网络前向反向传播

\qquad从最后一层的误差开始计算,误差是激活单元的预测( a k ( 4 ) ) (a_k^{(4)})(a k (4 )​)与实际值( y k ) (y^k)(y k )之间的误差,( k = 1 : K ) (k=1:K)(k =1 :K )。

\qquad用δ \delta δ表示误差,则有:
δ ( 4 ) = a ( 4 ) − y (2) \delta^{(4)}=a^{(4)}-y \tag{2}δ(4 )=a (4 )−y (2 )
\qquad利用这一层的误差去计算前一层的误差,换句话说,计算除最后一层外的误差需要用到后一层的误差。
δ ( 3 ) = ( Θ ( 3 ) ) T δ ( 4 ) ⋅ ∗ g ′ ( z ( 3 ) ) (3) \delta^{(3)}=\left(\Theta^{(3)}\right)^{T} \delta^{(4)} \cdot * g^{\prime}\left(z^{(3)}\right) \tag{3}δ(3 )=(Θ(3 ))T δ(4 )⋅∗g ′(z (3 ))(3 )
\qquad其中,g ′ ( z ( 3 ) ) g^{\prime}\left(z^{(3)}\right)g ′(z (3 ))是S i g m o i d Sigmoid S i g m o i d函数的导数,g ′ ( z ( 3 ) ) = a ( 3 ) ⋅ ∗ ( 1 − a ( 3 ) ) g^{\prime}\left(z^{(3)}\right)=a^{(3)} \cdot *\left(1-a^{(3)}\right)g ′(z (3 ))=a (3 )⋅∗(1 −a (3 )),z z z是未经过S函数的节点。( Θ ( 3 ) ) T δ ( 4 ) \left(\Theta^{(3)}\right)^{T} \delta^{(4)}(Θ(3 ))T δ(4 )是权重导致的误差的和。

\qquad同理得到:
δ ( 2 ) = ( Θ ( 2 ) ) T δ ( 3 ) ∗ g ′ ( z ( 2 ) ) (4) \delta^{(2)}=\left(\Theta^{(2)}\right)^{T} \delta^{(3)} * g^{\prime}\left(z^{(2)}\right) \tag{4}δ(2 )=(Θ(2 ))T δ(3 )∗g ′(z (2 ))(4 )
\qquad ( Θ ( n ) ) T δ ( n + 1 ) \left(\Theta^{(n)}\right)^{T} \delta^{(n+1)}(Θ(n ))T δ(n +1 )的解释如下图所示。由第2层的最后一个节点指出2个箭头,他参与到了第三层的这两个节点中,因此δ 2 ( 2 ) = θ 12 ( 2 ) ∗ δ 1 ( 3 ) + θ 22 ( 2 ) ∗ δ 2 ( 3 ) \delta_2^{(2)}=\theta_{12}^{(2)}\delta^{(3)}{1}+\theta{22}^{(2)}\delta^{(3)}_{2}δ2 (2 )​=θ1 2 (2 )​∗δ1 (3 )​+θ2 2 (2 )​∗δ2 (3 )​。δ ( 2 ) \delta^{(2)}δ(2 )可以理解为全部节点的误差,它的长度是3 3 3。

# 机器学习笔记(二)——手动编写神经网络前向反向传播

\qquad假设λ = 0 \lambda=0 λ=0,不做任何正则化处理时,有:∂ ∂ Θ i j ( l ) J ( Θ ) = a i j ( l ) δ i l + 1 \frac{\partial}{\partial \Theta_{i j}^{(l)}} J(\Theta)=a_{i j}^{(l)} \delta_{i}^{l+1}∂Θi j (l )​∂​J (Θ)=a i j (l )​δi l +1 ​,i i i是当前层的第i i i个节点的误差,j是对应到θ \theta θ的每一个值(上一层a a a的每一个值),δ l + 1 \delta^{l+1}δl +1就是和a l a^l a l挂钩的。

\qquad如果我们考虑正则化处理,并且我们的训练集是一个特征矩阵而非向量。在上面的特殊情况中,我们需要计算每一层的误差单元来计算代价函数的偏导数。在更为一般的情况中,我们同样需要计算每一层的误差单元,但是我们需要为整个训练集计算误差单元,此时的误差单元也是一个矩阵。我们用Δ i j ( l ) \Delta_{i j}^{(l)}Δi j (l )​来表示这个误差矩阵,第l l l层的第i i i个激活单元受到第j j j个参数影响而导致的误差。(举例:如果l l l层10个节点,l − 1 l-1 l −1层30个节点,那么这个误差矩阵的维度是( 10 , 30 ) (10,30)(1 0 ,3 0 ))。

\qquad算法表示为如下,其中,a表示每层激活函数后得到的值,m m m 表示样本数目。
f o r i = 1 : m { s e t a ( i ) = x ( i ) p e r f o r m f o r w a r d p r o p a g a t i o n t o c o m p u t e a ( l ) f o r l = 1 , 2 , 3 … . L U s i n g δ ( L ) = a ( L ) − y i p e r f o r m b a c k p r o p a g a t i o n t o c o m p u t e a l l p r e v i o u s l a y e r e r r o r v e c t o r Δ i j ( l ) : = Δ i j ( l ) + a j ( l ) δ i l + 1 } (5) \begin{aligned} for \quad i=1: m {\ &set \quad a^{(i)}=x^{(i)}\ &perform \,\,\, forward \,\,\, propagation \,\,\, to \,\,\, compute\,\,\, a^{(l)}\,\,\, for \,\,\,l=1,2,3 \ldots . L\ &Using \,\,\, \delta^{(L)}=a^{(L)}-y^{i}\ &perform \,\,\,back\,\,\, propagation\,\,\, to\,\,\, compute\,\,\, all\,\,\, previous \,\,\,layer\,\,\, error\,\,\, vector\ &\Delta_{i j}^{(l)}:=\Delta_{i j}^{(l)}+a_{j}^{(l)} \delta_{i}^{l+1}\ &} \end{aligned}\tag{5}f o r i =1 :m {​s e t a (i )=x (i )p e r f o r m f o r w a r d p r o p a g a t i o n t o c o m p u t e a (l )f o r l =1 ,2 ,3 ….L U s i n g δ(L )=a (L )−y i p e r f o r m b a c k p r o p a g a t i o n t o c o m p u t e a l l p r e v i o u s l a y e r e r r o r v e c t o r Δi j (l )​:=Δi j (l )​+a j (l )​δi l +1 ​}​(5 )

\qquad a j ( l ) δ i l + 1 a_{j}^{(l)} \delta_{i}^{l+1}a j (l )​δi l +1 ​这里是a j ( l ) a_{j}^{(l)}a j (l )​意味着是l l l层的所有节点a与l + 1 l+1 l +1层的第i i i个节点相乘->一个矩阵( m , n ) (m,n)(m ,n ) m m m是l + 1 l+1 l +1层节点数,n n n是l l l层节点数。

\qquad δ ( n ) \delta^{(n)}δ(n )是由本层伸出去的θ ( n ) \theta^{(n)}θ(n )算的。Δ i j ( l ) \Delta_{ij}^{(l)}Δi j (l )​的j j j的数目由节点a ( l ) a^{(l)}a (l )得到,i i i的数目由δ l + 1 \delta^{l+1}δl +1得到。计算方法也是本层( l ) (l)(l )的每个节点和( l + 1 ) (l+1)(l +1 )的第i i i误差相乘计算得到。由于a ( l ) a^{(l)}a (l )和δ l + 1 \delta^{l+1}δl +1挂钩,用它俩去算θ \theta θ的误差Δ \Delta Δ矩阵。δ ( l + 1 ) T ∗ a l \delta^{(l+1)T} * a^{l}δ(l +1 )T ∗a l (举例:( 1 , 26 ) T ∗ ( 1 , 401 ) − > ( 26 , 401 ) (1,26)^T * (1,401) -> (26,401)(1 ,2 6 )T ∗(1 ,4 0 1 )−>(2 6 ,4 0 1 ))。

\qquad区别:δ ( n ) \delta^{(n)}δ(n )以本层n n n为中心,看伸出去的去计算,Δ i j ( l ) \Delta_{ij}^{(l)}Δi j (l )​是以( l + 1 ) (l+1)(l +1 )层为中心(l l l层θ \theta θ就对应l + 1 l+1 l +1层)和上层的a a a相乘。

\qquad即首先用正向传播方法计算出每一层的激活单元,利用训练集的结果与神经网络预测的结果求出最后一层的误差,然后利用该误差运用反向传播法计算出直至第二层的所有误差。

\qquad加正则化后的梯度计算:
D i j ( l ) : = 1 m Δ i j ( l ) + λ Θ i j ( l ) if j ≠ 0 D i j ( l ) : = 1 m Δ i j ( l ) if j = 0 (6) \begin{aligned} &D_{i j}^{(l)}:=\frac{1}{m} \Delta_{i j}^{(l)}+\lambda \Theta_{i j}^{(l)} \quad \text { if } \quad j \neq 0\ &D_{i j}^{(l)}:=\frac{1}{m} \Delta_{i j}^{(l)} \quad \text { if } j=0 \end{aligned} \tag{6}​D i j (l )​:=m 1 ​Δi j (l )​+λΘi j (l )​if j ​=0 D i j (l )​:=m 1 ​Δi j (l )​if j =0 ​(6 )

代码实例

\qquad接下来,我将用逻辑回归来解决经典的MNIST手写数字识别问题,下面是它详细的代码介绍。

\qquad首先载入数据集:

import numpy as np
from scipy.io import loadmat

data = loadmat('ex4data1.mat')
X = data['X']
y = data['y']

\qquad对y标签进行一次one-hot 编码。

\qquadone-hot 编码将类标签n(k类)转换为长度为k的向量,其中索引n为”hot”(1),而其余为0。

\qquadScikitlearn有一个内置的实用程序,我们可以使用这个。

from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(sparse=False)
y_onehot = encoder.fit_transform(y)
print(y_onehot.shape)

\qquad我们要为此练习构建的神经网络具有与我们的实例数据(400 +偏置单元)大小匹配的输入层,25个单位的隐藏层(带有偏置单元的26个),以及一个输出层(10个单位对应我们的一个one-hot编码类标签)。

\qquad我们首先需要实现的是评估一组给定的网络参数的损失的代价函数:

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

def forward_propagate(X, theta1, theta2):

    m = X.shape[0]

    a1 = np.insert(X, 0, values=np.ones(m), axis=1)

    z2 = a1 * theta1.T

    a2 = np.insert(sigmoid(z2), 0, values=np.ones(m), axis=1)
    z3 = a2 * theta2.T
    h = sigmoid(z3)

    return a1, z2, a2, z3, h

def cost(params, input_size, hidden_size, num_labels, X, y, learning_rate):
    m = X.shape[0]
    X = np.matrix(X)
    y = np.matrix(y)

    theta1 = np.matrix(np.reshape(params[:hidden_size * (input_size + 1)], (hidden_size, (input_size + 1))))
    theta2 = np.matrix(np.reshape(params[hidden_size * (input_size + 1):], (num_labels, (hidden_size + 1))))

    a1, z2, a2, z3, h = forward_propagate(X, theta1, theta2)

    J = 0

    for i in range(m):
        first_term = np.multiply(-y[i, :], np.log(h[i, :]))
        second_term = np.multiply((1 - y[i, :]), np.log(1 - h[i, :]))
        J += np.sum(first_term - second_term)

    J = J / m

    return J

def cost_n(params, input_size, hidden_size, num_labels, X, y, learning_rate):
    m = X.shape[0]
    X = np.matrix(X)
    y = np.matrix(y)

    theta1 = np.matrix(np.reshape(params[:hidden_size * (input_size + 1)], (hidden_size, (input_size + 1))))
    theta2 = np.matrix(np.reshape(params[hidden_size * (input_size + 1):], (num_labels, (hidden_size + 1))))

    a1, z2, a2, z3, h = forward_propagate(X, theta1, theta2)

    J = 0
    for i in range(m):
        first_term = np.multiply(-y[i, :], np.log(h[i, :]))
        second_term = np.multiply((1 - y[i, :]), np.log(1 - h[i, :]))
        J += np.sum(first_term - second_term)

    J = J / m

    J += (float(learning_rate) / (2 * m)) * (np.sum(np.power(theta1[:, 1:], 2)) + np.sum(np.power(theta2[:, 1:], 2)))

    return J

\qquad定义反向传播。

\qquad它不仅计算了当前的代价,还返回了参数的梯度。


def backprop(params, input_size, hidden_size, num_labels, X, y, learning_rate):
    m = X.shape[0]
    X = np.matrix(X)
    y = np.matrix(y)

    theta1 = np.matrix(np.reshape(params[:hidden_size * (input_size + 1)], (hidden_size, (input_size + 1))))
    theta2 = np.matrix(np.reshape(params[hidden_size * (input_size + 1):], (num_labels, (hidden_size + 1))))

    a1, z2, a2, z3, h = forward_propagate(X, theta1, theta2)

    J = 0
    delta1 = np.zeros(theta1.shape)
    delta2 = np.zeros(theta2.shape)

    for i in range(m):
        first_term = np.multiply(-y[i, :], np.log(h[i, :]))
        second_term = np.multiply((1 - y[i, :]), np.log(1 - h[i, :]))
        J += np.sum(first_term - second_term)

    J = J / m

    J += (float(learning_rate) / (2 * m)) * (np.sum(np.power(theta1[:, 1:], 2)) + np.sum(np.power(theta2[:, 1:], 2)))

    for t in range(m):
        a1t = a1[t, :]
        z2t = z2[t, :]
        a2t = a2[t, :]
        ht = h[t, :]
        yt = y[t, :]

        d3t = ht - yt

        z2t = np.insert(z2t, 0, values=np.ones(1))
        d2t = np.multiply((theta2.T * d3t.T).T, sigmoid_gradient(z2t))

        delta1 = delta1 + (d2t[:, 1:]).T * a1t
        delta2 = delta2 + d3t.T * a2t

    delta1 = delta1 / m
    delta2 = delta2 / m

    delta1[:, 1:] = delta1[:, 1:] + (theta1[:, 1:] * learning_rate) / m
    delta2[:, 1:] = delta2[:, 1:] + (theta2[:, 1:] * learning_rate) / m

    grad = np.concatenate((np.ravel(delta1), np.ravel(delta2)))

    return J, grad

\qquad测试梯度下降。输出结果为:6.732102294368071 (10285,)。这里直接计算了m个样本下,随机θ \theta θ下的代价和梯度。并没有最优化,梯度下降,更新参数的过程。


J, grad = backprop(params, input_size, hidden_size, num_labels, X, y_onehot, learning_rate)
print(J, grad.shape)

\qquad初始化网络,定义参数。


input_size = 400
hidden_size = 25
num_labels = 10
learning_rate = 1

params = (np.random.random(size=hidden_size * (input_size + 1) + num_labels * (hidden_size + 1)) - 0.5) * 0.25

m = X.shape[0]
X = np.matrix(X)
y = np.matrix(y)
print(params.shape)

theta1 = np.matrix(np.reshape(params[:hidden_size * (input_size + 1)], (hidden_size, (input_size + 1))))
theta2 = np.matrix(np.reshape(params[hidden_size * (input_size + 1):], (num_labels, (hidden_size + 1))))

print(theta1.shape, theta2.shape)

\qquad开始预测。先最小化目标函数得到训练后θ \theta θ的数值。


from scipy.optimize import minimize

fmin = minimize(fun=backprop, x0=params, args=(input_size, hidden_size, num_labels, X, y_onehot, learning_rate),
                method='TNC', jac=True, options={'maxiter': 250})
print(fmin)

X = np.matrix(X)
theta1 = np.matrix(np.reshape(fmin.x[:hidden_size * (input_size + 1)], (hidden_size, (input_size + 1))))
theta2 = np.matrix(np.reshape(fmin.x[hidden_size * (input_size + 1):], (num_labels, (hidden_size + 1))))

\qquad输入数据得出预测的y值,并计算准确率。

a1, z2, a2, z3, h = forward_propagate(X, theta1, theta2)
y_pred = np.array(np.argmax(h, axis=1) + 1)

correct = [1 if a == b else 0 for (a, b) in zip(y_pred, y)]
accuracy = (sum(map(int, correct)) / float(len(correct)))
print ('accuracy = {0}%'.format(accuracy * 100))

\qquad最终计算得出,达到的准确率为:accuracy = 99.44%。

Original: https://blog.csdn.net/Mars1533/article/details/124432136
Author: 无情码手
Title: # 机器学习笔记(二)——手动编写神经网络前向反向传播



相关阅读

Title: 编程语言居然是魔法咒语!

作为一个本职是程序员的魔幻爱好人士,THE DAY,我终于意识到了!

如果你不懂魔法的麻瓜,没关系,让我一点点来揭开这个世纪大奥妙。

[En]

If you don’t know the Muggle of magic, it doesn’t matter, let me unravel the great mystery of this century little by little.

首先我们从最简单的HTML 开始,这个在技术上称之为”标记语言”,或者叫结构性语言。

炼金术

这种 标记性语言,由众多的成对标记组成,这其实是一种非常伟大的炼金术魔法!

由简单的语言标记组成的魔法阵,在魔法阵中的所有物件都会附加特定的魔法效果,或者被转换成其他物件。

[En]

In a magic array made up of simple language tags, all objects in the magic array are attached to specific magic effects, or are converted into other objects.

看过钢之炼金术师的友人们应该非常理解这种炼金术。

除此之外,炼金术的第二个必备条件,就是 “运行环境”

这个其实也是所有魔法都需要具备的条件,就是所在世界的基本运行法则。

[En]

In fact, this is also a necessary condition for all magic, which is the basic law of operation of the world in which it lives.

众所周知,在不同的魔法体系里面,都有这不同的世界法则,在我们这个贫瘠的位面,并没有那些魔幻体系里具备的”魔力””灵力”等基本要素。

[En]

As we all know, there are different laws of the world in different magical systems, and in our barren plane, there are no basic elements such as “magic” and “spirituality” in magical systems.

所以就算你手里面拿着一本咒语百科,任意一条都不会起作用。

那么回到HTML,刚才那段咒语的执行环境什么?

聪明的小伙伴一定已经猜出来了,没错,就是称之为”浏览器”的东西。

这是一个虚拟容器,只要使用恰当的方式,把刚才那段咒语放入这个容器中,就会实现它原本的作用。

[En]

This is a virtual container, as long as the appropriate way, just put the spell into this container, it will achieve its original effect.

如下:

没错,这是一条把文本加粗放大的功能,当然这是一个无足轻重的小技巧而已。

[En]

Yes, this is a function to bold and enlarge the text, but of course it’s a trivial trick.

但是如果你真的要把恶魔召唤出来,当然就复杂程度而言,可能会让我们跑题太远。

[En]

But if you really want to summon the devil, of course, in terms of complexity, it may take us too far.

接下来让我们玩点别的。

召唤术

让我们召唤一只小白鸽的咒语

从虚无之中构建物品,因为现在这个白鸽只能说是纸片白鸽,所以我们叫静态”物品”,当然还有动态”物品”。能让它动起来的咒语,叫”动画编程”

[En]

Construct objects from nothingness, because now this white pigeon can only be said to be a piece of paper pigeon, so we call it static “object” and, of course, dynamic “object”. The spell that makes it work is called “Animation programming.”

这里已经脱离了炼金术的范畴了,已经不是”等价交换”。

低级别的简易魔法咒语长度较短,高级别的魔法常常功能强大,但是咒语也会非常冗长。

[En]

Low-level simple magic spells are short in length, high-level magic is often powerful, but spells can also be very lengthy.

在实战中,如果所有的功能都从零开始构建,那么会耗费非常多的时间,在瞬息万变的战争等同于一个固定靶子。这就是

[En]

In actual combat, if all the functions are built from scratch, it will take a lot of time, and in a rapidly changing war, it is equivalent to a fixed target. That’s it

吟唱时间

而且吟唱过程中非常容易受到干扰,这些干扰可能来自于自身能力不足,对编程技术的熟练度不够,或者对所要达成功能理解不足,对环境理解不足等等,造成了BUG。

修复BUG的过程会继续增加吟唱时间,也有来自于外部的干扰,比方说友军”PM”的对话信息,来自战场的干扰等。

那么为了解决时间上的问题,经验老道的魔法师会事先准备一些

魔法卷轴

这些卷轴是在平常空闲之余花费更大量的时间与精力把一些实用的复杂咒语刻印在卷轴中。

[En]

These scrolls spend more time and energy in their spare time to engrave some practical complex spells in the scrolls.

在编程上称之为”JDK” 或者工具包 或者API。实际使用时,只需要一句较短的激活咒语就能开启很复杂的功能。

当然这些卷轴的制作者并不一定是使用者,也可能他人制作,使用者通过购买,或者免费下载。

[En]

Of course, the makers of these scrolls are not necessarily users, but also may be made by others, users through the purchase, or free download.

有一些比较常见的卷轴协会,我们称之为开源平台,可以免费获得卷轴,而且这些卷轴的咒语细节也是开放给所有观看者免费浏览,简直良心。

[En]

There are some of the more common scroll associations, which we call the open source platform, where scrolls are available for free, and the spell details of these scrolls are open to all viewers for free viewing.

那么下面我们来谈另外一个话题

魔法阵

魔法阵跟咒语的区别在于一个是图案,一个是口述语言。

那么在编程语言中用图案的方式来实现功能的方法是什么?

没错!就是”可视化编程”,通过特定的工具”IDE”拖拉组件来构建应用。

各种可视化编程工具,VS, Unity3D,dreamWeaver

魔法阵的本质还是咒语,所以所有可视化编程还是可以通过手动写代码实现,只是开发效率上会有一定优势。

[En]

The essence of the magic array is still a spell, so all visual programming can still be achieved by writing code manually, but there will be some advantages in development efficiency.

刚才我们提到”口述语言”,实质上编程语言不需要念出来,由刚才开头的简易例子大家已经知道了,是需要放入到一个虚拟容器之中才能生效。

当然不同的语言也会有不同的容器。

网页类的 包括html css js 都需要浏览器环境,c# 需要”.netframwork” ,java语言需要”jvm”

还有一种很特别的施法技巧,叫

默念施法

施法者可以在心里默念咒语,就可以通过不发声并且非常快速的释放咒语。

[En]

The spell caster can recite the spell in his mind and can release the spell very quickly by being silent.

在编程之中,这叫做”ctrl+c ctrl+v” 通过复制粘贴快速使用一些已有代码片段。

我们知道大型的禁咒魔法通常需要

多人协助施法

为了解决多人协作过程,并且复杂度太高的问题。

开发人员想出来一些办法,比如”面向对象” “二十三设计模式” “面向领域” “面向方面”

这个时候会有另外一种职能被划分出来,”架构师”,大多数时架构师并不在画面上,而是在看不见地方默默设计,推演。

到这里,看到这个风阵的画面可能有些小伙伴就想起一个基本问题了。

[En]

At this point, some of my friends may think of a basic question when they see the picture of the wind.

那么对应

元素魔法师

如风火水雷等元素系的魔法,在编程上又是如何体现的?

首先在某些魔法体系里,比如西方魔幻,WOW,魔戒,龙枪系列等,元素系是属于比较常见魔法。

通常他们的”运行环境”的基本要素就是这些游离元素”风””火””水””雷”散布在整个世界,是世界的基本法则。

那么在编程里的虚拟世界之中,最最基本当然是”0″”1″ , 基本元素之上还有复合型的元素,比如风+电= 雷暴 ,水+火=雾

0 与 1 会组合出另外一些逻辑性元素:”与””或””非””异或”等逻辑概念,再往上一层就是由CPU指令与这些逻辑符号组合而成的”汇编语言”元素,左移,右移,存入,复制等

然后再往上一层,就到了我们熟知的高级语言范畴, 变量,数组,对象,循环等等。

暗黑系的破坏类魔法对应黑客,光明系的治愈性魔法对应防御类的白帽子。

[En]

The destruction magic of the dark system corresponds to the hacker, and the healing magic of the light department corresponds to the white hat of the defense class.

专攻药物的魔药师对应插件开发工程师。

这就是 现代魔法学,编程开发。

一门可以无中生有并且非常严谨的规则推演学科。

可以制造奇幻,也可以毁天灭地。可以点石成金,也可以瞬息千里。

[En]

It can create fantasy, or it can destroy heaven and earth. It can turn stone into gold, or it can be thousands of miles away.

太过美妙!

Original: https://www.cnblogs.com/7rhythm/p/10428889.html
Author: 鬼柒
Title: 编程语言居然是魔法咒语!

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/244955/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

最近整理资源【免费获取】:   👉 程序员最新必读书单  | 👏 互联网各方向面试题下载 | ✌️计算机核心资源汇总