简介:研究者示意,因为宽泛的科学计算和机器学习畛域在底层构造上都须要线性代数的反对,因而有可能以可微编程的模式,发明一种新的编程思维。上面,咱们就一起进入这个全新的畛域。什么是可微编程通过动画、动效减少 UI 表现力,作为前端或多或少都做过。这里以弹性阻尼动画的函数为例:
函数在
时是成果最好的。最终,实现成 JavaScript 代码:
function damping(x, max) {
let y = Math.abs(x);
// 上面的参数都是来源于公式用数值拟合的后果
y = 0.82231 * max / (1 + 4338.47 / Math.pow(y, 1.14791));
return Math.round(x < 0 ? -y : y);
}
如图 1 所示,2019 年 Julia Computing 团队发表论文《A Differentiable Programming System to Bridge Machine Learning and Scientific Computing》示意他们构建了一种可微编程零碎,它能将主动微分内嵌于 Julia 语言,从而将其作为 Julia 语言的第一公民。如果将可微编程零碎视为编程语言第一公民,那么不论是机器学习还是其它科学计算都将不便不少。Y Combinator Research 研究者 Michael Nielsen 对此十分兴奋,十分同意 Andrej Karpathy 的观点。Karpathy 说:「咱们正向前迈出了一步,与原来对程序有残缺的定义不同,咱们当初只是写一个大抵的解决问题的框架,这样的框架会通过权重把解决过程参数化。如果咱们有一个好的评估规范,那么最优化算法就能帮咱们找到更好的解(参数)。」这里 Karpathy 说的就是传统编程和可微编程的区别,可微编程会通过梯度降落等最优化办法主动搜寻最优解。但这里有个问题,程序须要梯度能力向着最优后退,因而程序的很多局部都要求是可微的。鉴于这一点,很多人也就将机器学习(ML)称说为可微编程了。然而可微编程只能用于机器学习吗?它能不能扩大到其它畛域,甚至成为编程语言的根本个性?答案是能够的,这就是 Julia 团队及 MIT 等其余钻研机构正在尝试的。近年来,机器学习模型越来越精妙,展现出了很多科学计算的个性,侧面凸显了机器学习框架的弱小能力。研究者示意,因为宽泛的科学计算和机器学习畛域在底层构造上都须要线性代数的反对,因而有可能以可微编程的模式,发明一种新的编程思维。上面,咱们就一起进入这个全新的畛域。▐ 可微编程和主动微分的关系 在开始可微编程之前,我想先简略回顾一下之前计算弹性阻尼的例子。你会发现
数值微分法:
只有 h 取很小的数值,比方 0.0001,那么咱们能够很不便求解导数,并且能够对用户暗藏求解过程,用户只有给出指标函数和要求解的梯度的变量,程序能够主动给出相应的梯度,这也是某种意义上的“主动微分”。而咱们在 H5 弹性阻尼函数的求解过程中,应用的正式这种“主动微分”技术。数值微分法的弊病在于计算量大,因为是做拟合相当于咱们要把公式
符号微分(Symbolic Differentiation)属符号计算的领域,其计算结果是导函数的表达式。符号计算用于求解数学中的公式解(也称解析解)。和前文数值解不同的是,通过符号位分失去的是解的表达式而非具体的数值。依据根本函数的求导公式以及四则运算、复合函数的求导法令,符号微分算法能够失去任意可微函数的导数表达式。
而后将自变量的值代入导数公式,失去任意点处的导数值。符号微分计算出的表达式须要用字符串或其余数据结构存储,如表达式树。数学软件如 Mathematica,Maple,matlab 中实现了这种技术,python 语言的符号计算库也提供了这类算法。对于深层复合函数,如机器学习中神经网络的映射函数,符号微分算法失去的导数计算公式将会十分简短。这种简短的状况,咱们称为表达式收缩(expression swell)。对于机器学习中的利用,不须要失去导数的表达式,而只需计算函数在某一点处的导数值,从而以参数的模式更新神经元的权重。因而,符号微分在计算冗余且老本昂扬。例如:对于公式
如果采纳符号微分算法,当 n =1,2,3,4 时的 ln 及其导数如图 3 所示。
主动微分不同于数值微分和符号位分,它是介于符号微分和数值微分之间的一种办法。数值微分一开始就代入数值近似求解,而符号微分间接对表达式进行推导,最初才代入自变量的值得到最终解。主动微分则是将符号微分利用于最根本的运算(或称原子操作),如常数,幂函数,指数函数,对数函数,三角函数等根本函数,代入自变量的值得到其导数值,作为两头后果进行保留。而后,再依据这些根本运算单元的求导后果计算出整个函数的导数值。主动微分的灵便强,可实现齐全向用户暗藏求导过程,因为它只对根本函数或常数使用符号微分法令,因而能够灵便地联合编程语言的循环、分支等构造,依据链式法则,借助于计算图计算出任意简单函数的导数值。因为存在上述长处,该办法在古代深度学习库中失去宽泛应用。Julia 的论文实现的 zygote 工具进行的可微编程,钻研人员定义了一个损失函数,将点光源作为输出在图像上产生光照,如图 4 所示和参考图像进行比照。通过可微编程的形式主动计算和提取梯度,并用于更新点光源的地位:julia> guess = PointLight(Vec3(1.0), 20000.0, Vec3(1.0, 2.0, -7.0))
julia> function loss_function(light)
rendered_color = raytrace(origin, direction, scene, light, eye_pos)
rendered_img = process_image(rendered_color, screen_size.w,
screen_size.h)
return mean((rendered_img .- reference_img).^2)
end
julia> gs = gradient(x -> loss_function(x, image), guess)
可微编程实现智能应用程序 在 Julia 之后 Swift 也推出了本人的可微编程,对,你没听错,就是 Apple 利用开发举荐编程语言 Swift。在 Swift 的可微编程提案中,钻研人员提出了“智能应用程序”的概念。智能应用程序很智能:它们应用机器学习技术来加强用户体验。智能应用程序能够借助可微编程的弱小能力,对利用的行为做出预测、提供倡议并理解用户偏好,依据用户偏好智能调整利用的行为。智能利用的外围是可微编程,主动微分可用于通过梯度降落系统地优化(即找到“好”值)参数。通过传统编程思维的算法优化,这些参数通常很难解决,要么是相似数值微分和符号微分中那样参数太多,要么是难以和利用的行为做关联。例如,咱们要开发一个智能播放器,它尝试依据播放器内容类型和播放器的用户偏好主动调整播放速度。
enum PodcastCategory {
case comedy
case news
...
}
enum PodcastSection {
case advertisement
case introduction
case body
case conclusion
}
struct PodcastState {
let category: PodcastCategory
let section: PodcastSection
}
struct PodcastSpeedModel {
var minSpeed, maxSpeed: Float
var categoryMultipliers: [PodcastCategory: Float]
var sectionMultipliers: [PodcastSection: Float]
/// Returns a podcast speed multiplier prediction for the given podcast category
/// and section.
func prediction(for state: PodcastState) -> Float {let speed = categoryMultipliers[state.category] * sectionMultipliers[state.section]
if speed < minSpeed {return minSpeed}
if speed > maxSpeed {return maxSpeed}
return speed
}
} 此播放器的播放速度参数为:minSpeed、maxSpeed、categoryMultipliers 和 sectionMultipliers。依据咱们的教训来判断的话,什么是好的参数?不同的用户可能会有不同的答案,无奈依据用户偏好设定不同的参数值。智能应用程序能够借助可微编程来确定个性化的参数值,如下:让用户手动设置速度,并在用户扭转速度时记录察看后果的参数值。在收集到足够的察看值后,搜寻参数值,使模型预测的速度靠近用户的首选速度。如果找到这样的值,播放器会产生预测并主动设置速度。“梯度降落”是执行这种搜寻的算法,而反对可微编程的语言能够很容易地实现梯度降落。这是一些阐明梯度降落的伪代码:
// 首先,咱们须要一个梯度降落的指标函数来最小化,这里应用均匀绝对误差:
struct Observation {
var podcastState: PodcastState
var userSpeed: Float
}
func meanError(for model: PodcastSpeedModel, _ observations: [Observation]) -> Float {
var error: Float = 0
for observation in observations {error += abs(model.prediction(for: observation.podcastState) - observation.userSpeed)
}
return error / Float(observations.count)
}
// 接下来,咱们实现梯度降落算法。
var model = PodcastModel()
let observations = storage.observations()
for _ in 0..<1000 {
// The language differentiates `meanError` to get a "gradient", which is a value indicating
// how to change `model` in order to decrease the value of `meanError`.
let gradient = gradient(at: model) {meanError(for: $0, observations) }
// Change `model` in the direction that decreased the value of `meanError`.
model -= 0.01 * gradient
} 至此,咱们就应用可微编程的能力实现了智能利用的要害局部,这个利用会随着用户理论应用过程越来越智能、越来越懂用户的偏好,从而主动帮忙用户设置播放速度。除了用可微编程来实现前文中播放速度智能化管制外,动画参数的变化率、应用物理方程在游戏引擎中的可微函数建模、游戏中的 NPC 用可微编程实现和玩家之间智能的互动、流体和其余物理过程的模仿技术基于用导数定义的方程的近似解等等。机器人和机械工程中应用的主动控制算法也依赖于对关节和其余物理零碎的行为进行建模,可微编程能够轻松失去这些模型函数的导数。只有你违心,你简直能够在所有编程场景里用可微编程的思维来智能化解决问题。用工具方程拟合残缺的代码能够从:https://github.com/JunreyCen/… 这里找到,JunreyCen 在这里具体的介绍了他钻研和实现弹性阻尼动画的过程。为了解决计算动画变动速率参数的问题,他应用了 4PL:
进行函数拟合。用到的是一个网站提供的工具,这个网站的网址是:http://www.qinms.com/webapp/c…。您能够在这里通过把 JunreyCen 采集的,iOS 动画数据输出来取得 4PL 里 A、B、C、D 四个参数的具体值。train_x = (0,500,1000,1500,2500,6000,8000,10000,12000)
train_y = (0,90,160,210,260,347.5,357.5,367.5,377.5)
有了 A、B、C、D 四个参数以及 4PL 公式:
您就能够失去之前 JavaScript 代码里的弹性阻尼算法,从而实现 iOS 一样的弹性阻尼成果。到这里,您可能会想:可能做弹性动画挺酷的但还有什么用呢?其实,只有稍作形象咱们就能把这个办法提炼成解决动画的通用办法。例如设计师在 AfterEffect 里制作了一段儿动画,您只须要在动画的关键帧上采集一系列的数据,而后用函数拟合的办法找到对应的参数和函数,再将参数和函数融入到程序算法中,就可能通过它们来管制动画成果了。多项式拟合这种全新的动画编程形式和可微编程有什么关系呢?其实,用一个 3rd-party 的工具咱们既不晓得它是如何工作的,也不不便集成到工程项目中去,因而,你能够用可微编程疾速实现一个本人的函数拟合算法。因为之前围绕 TensorFlow 的主动微分介绍可微编程,这里我就介绍一下如何用可微编程实现多项式拟合。
讲完实践局部,上面就让咱们用可微编程在 TensorFlow 里实操一遍。环境筹备和之前一样:# 装置 MiniConda 创立环境:并更新:pip install -U pip
conda install -c apple tensorflow-deps
pip uninstall tensorflow-macos
pip uninstall tensorflow-metal
进入工作目录启动 Jupyter Notebook 进行试验
jupyter notebook 在 Notebook 里筹备 TensorFlow 环境:import tensorflow as tf
Resets notebook state
tf.keras.backend.clear_session()
print(“Version: “, tf.__version__)
print(“Eager mode: “, tf.executing_eagerly())
print(
"GPU is",
"available" if tf.config.list_physical_devices("GPU") else "NOT AVAILABLE") 输入:Version: 2.8.0
Eager mode: True
GPU is available 能够看到,我应用的 TensorFlow 是 2.8.0 版本,因为笔记本里 M1 Pro 有性能强劲的 GPU,在 GPU is available 加持下,训练速度十分快。筹备数据:
train_x = [0,500,1000,1500,2500,6000,8000,10000,12000]
train_y = [0,90,160,210,260,347.5,357.5,367.5,377.5] 开始可微编程:
optimizer = tf.keras.optimizers.Adam(0.1)
t_x = tf.constant(train_x,dtype=tf.float32)
t_y = tf.constant(train_y,dtype=tf.float32)
wa = tf.Variable(0.,dtype = tf.float32,name=’wa’)
wb = tf.Variable(0.,dtype = tf.float32,name=’wb’)
wc = tf.Variable(0.,dtype = tf.float32,name=’wc’)
wd = tf.Variable(0.,dtype = tf.float32,name=’wd’)
variables = [wa,wb,wc,wd]
for e in range(num):
with tf.GradientTape() as tape:
#预测函数
y_pred = tf.multiply(wa,t_x)+tf.multiply(w2,tf.pow(t_x,2))+tf.multiply(wb,tf.pow(t_x,2))+tf.multiply(wc,tf.pow(t_x,2))+wd
#损失函数
loss=tf.reduce_sum(tf.math.pow(y_pred-t_y,2))
#计算梯度
grads=tape.gradient(loss, variables)
#更新参数
optimizer.apply_gradients(grads_and_vars=zip(grads,variables))
if e % 100 == 0:
print("step: %i, loss: %f, a:%f, b:%f, c:%f, d:%f" % (e,loss,wa.numpy(),wb.numpy(),wc.numpy(),wd.numpy()))
在下面的代码里有几个要害的局部须要留神:optimizer、wa,wb,wc,wd、loss、grads、apply_gradients 这五个局部。optimizer 是优化器,罕用的有 SGD 和 Adam,前者是梯度降落后者则能够了解为加强版,对于本示例只有 9 条数据这种样本比拟少的状况有奇效。因为 Adam 对每个参数应用雷同的学习率,并随着学习的进行而独立地适应。此外,Adam 是基于动量的算法,利用了梯度的历史信息。基于这些特色,我抉择了 Adam 而非 SGD。其次,须要关注的是用 tf.Variable(0.,dtype = tf.float32,name=’wa’) 来申明咱们要训练的参数 wa,wb,wc,wd,这部分在后面介绍主动微分的时候说过。loss 损失函数则是用拟合后果比照理论后果的方差来定义的,也叫均匀方差损失。梯度局部则利用 TensorFlow 主动微分梯度带计算方法 tape.gradient(loss, variables) 来进行计算。最初,apply_gradients 把咱们计算好的梯度 grads 利用到参数下来进行下一轮的拟合。咱们反复这个过程 10000 次,一直的优化参数来迫近理论数据:
step: 0, loss: 4.712249, a:0.100003, b:0.100003, c:0.100003, d:0.100003
step: 100, loss: 0.164529, a:1.204850, b:-0.219918, c:-0.219918, d:0.294863
step: 200, loss: 0.082497, a:1.994068, b:-0.615929, c:-0.615929, d:0.209093
step: 300, loss: 0.073271, a:2.291683, b:-0.766129, c:-0.766129, d:0.176420
…
step: 9700, loss: 0.072893, a:2.371203, b:-0.804242, c:-0.804242, d:0.169179
step: 9800, loss: 0.072850, a:2.369858, b:-0.805587, c:-0.805587, d:0.167835
step: 9900, loss: 0.072853, a:2.369503, b:-0.805943, c:-0.805943, d:0.167479
训练好之后,把后果打印进去看看实际效果:
plt.scatter(t_x,t_y,c=’r’)
y_predict = tf.multiply(wa,t_x)+tf.multiply(w2,tf.pow(t_x,2))+tf.multiply(wb,tf.pow(t_x,2))+tf.multiply(wc,tf.pow(t_x,2))+wd
print(y_predict)
plt.plot(t_x,y_predict,c=’b’)
plt.show()
输入:
tf.Tensor(
[0.16805027 0.2640069 0.3543707 0.4391417 0.59190494 0.95040166
1.0322142 1.0245409 0.92738193], shape=(9,), dtype=float32) 因为训练数据和预测的数据都做了归一化,须要解决一下能力看到实在后果:
print((y_predict.numpy())377.5)
输入:
63.43898 99.66261 133.77495 165.77599 223.4441 358.77664 389.66086 386.7642 350.08667
实在数据:
train_y = [0,90,160,210,260,347.5,357.5,367.5,377.5] 从图 6 中能够看到,咱们曾经借助多项式拟合和可微编程胜利“迫近”了实在数据,图中红点代表实在数据,蓝色线条是依据预测后果绘制。
scipy 用 curve_fit 拟合 4PL 因为数据量太小,应用多项式拟合的时候,大略在第三轮之后 loss 的变动就不大了,可见多项式拟合对咱们要解决的弹性阻尼函数问题并非最优解。在通过了一番钻研,我找到 python 的科学计算工具包 scipy,在 optimizer 里有一个 curve_fit 工具成果很好,可能把咱们的 4PL 公式
from scipy import stats
import scipy.optimize as optimization
xdata = t_x
ydata = t_y
def fourPL(x, A, B, C, D):
return ((A-D)/(1.0+((x/C)**(B))) + D)
guess = [0, -0.3, 0.7, 1]
params, params_covariance = optimization.curve_fit(fourPL, xdata, ydata, guess)#, maxfev=1)
x_min, x_max = np.amin(xdata), np.amax(xdata)
xs = np.linspace(x_min, x_max, 1000)
plt.scatter(xdata, ydata)
plt.plot(xs, fourPL(xs, *params))
plt.show() 如图 7 所示函数拟合的成果很好,可见,用 scipy 提供的新办法 optimization.curve_fit() 进行可微编程和应用 TensorFlow 区别不大,也是针对函数里的参数进行训练,返回 params 代表训练后的参数。
print(*params)
输入:# 应用工具失去的参数:
A = 410.517236432893 A = 405.250160538176
B = 1.1531891657236022 B = -1.17885294211307
C = 1481.6957597831604 C = 1414.70383142617
D = -0.16796047781164916 D = -0.516583385175963
比照 3rd-party 工具产出的参数:y = 0.82231 * max / (1 + 4338.47 / Math.pow(y, 1.14791)); 最终,程序算法上的差别很小。我应用:https://github.com/JunreyCen/… 的代码进行理论运行测试,两者理论体现上差别不大,成果都挺好的。您在应用示例的时候要留神一下,对:<script src=”https://cdn.jsdelivr.net/npm/vue”></script> 做个小批改:<script src=”http://static.runoob.com/assets/vue/1.0.11/vue.min.js”></script> 即可。神经网络的拟合 尽管,到这里仿佛曾经实现了可微编程解决弹性阻尼函数拟合的问题,并找到了程序的算法解决方案,然而,回到智能化编程思维里,我脑海里浮现一个问题:图计算和神经网络是否解决这个问题呢?带着这个问题,我又定义了一个图计算的神经网络:
model = tf.keras.Sequential()
增加层
注:input_dim(输出神经元个数) 只须要在输出层器重设置,前面的网络能够主动推断出该层的对应输出
model.add(tf.keras.layers.Dense(units=10, input_dim=1, activation=’selu’))
model.add(tf.keras.layers.Dense(units=10, input_dim=1, activation=’selu’))
model.add(tf.keras.layers.Dense(units=10, input_dim=1, activation=’selu’))
model.add(tf.keras.layers.Dense(units=10, input_dim=1, activation=’selu’))
model.add(tf.keras.layers.Dense(units=10, input_dim=1, activation=’selu’))
model.add(tf.keras.layers.Dense(units=10, input_dim=1, activation=’selu’))
model.add(tf.keras.layers.Dense(units=10, input_dim=1, activation=’selu’))
model.add(tf.keras.layers.Dense(units=10, input_dim=1, activation=’selu’))
model.add(tf.keras.layers.Dense(units=10, input_dim=1, activation=’selu’))
model.add(tf.keras.layers.Dense(units=10, input_dim=1, activation=’selu’))
神经元个数 输出神经元个数 激活函数
model.add(tf.keras.layers.Dense(units=1, activation=’selu’))
输入神经元个数
2 设置优化器和损失函数
model.compile(optimizer=’adam’, loss=’mse’)
优化器 损失函数 (均方误差)
3 训练
history = model.fit(t_x, t_y,epochs=2000)
4 预测
y_pred = model.predict(t_x) 这里我用 TensorFlow 的 keras API 定义了十个 Dense 层,每层有十个神经元、一个输出维度和 selu 非线性激活函数,再定义了一个神经元同样激活函数的 Dense 层作为输入。这个定义是通过试验发现过多或过少的神经元都无奈晋升训练成果,太多的层也无奈更好的优化准确率。模型的优化器如后面的示例也是 adam,损失函数也是均匀方差损失函数 mse。训练过程:
1/1 [==============================] – 1s 573ms/step – loss: 74593.5156
Epoch 2/2000
1/1 [==============================] – 0s 38ms/step – loss: 74222.5469
Epoch 3/2000
1/1 [==============================] – 0s 31ms/step – loss: 74039.7734
…
Epoch 1994/2000
1/1 [==============================] – 0s 26ms/step – loss: 0.3370
Epoch 1995/2000
1/1 [==============================] – 0s 25ms/step – loss: 0.3370
Epoch 1996/2000
1/1 [==============================] – 0s 24ms/step – loss: 0.3370
Epoch 1997/2000
1/1 [==============================] – 0s 26ms/step – loss: 0.3370
Epoch 1998/2000
1/1 [==============================] – 0s 24ms/step – loss: 0.3370
Epoch 1999/2000
1/1 [==============================] – 0s 25ms/step – loss: 0.3369
Epoch 2000/2000
1/1 [==============================] – 0s 24ms/step – loss: 0.3369
能够看到,整个神经网络的训练过程继续稳固的晋升,始终到近 2000 个 Epoch 的时候 loss 损失还在持续降落,可见持续训练还能进一步晋升拟合成果,更好的迫近实在数据。依照治理,咱们画个图来察看一下成果:
5 画图
plt.scatter(xdata, ydata)
plt.plot(t_x, y_pred, ‘r-‘, lw=5)
plt.show() 如图 8 所示,训练好的神经网络预测后果和实在数据比照简直是统一的,然而,比照之前 4PL 和多项式的函数拟合,整体成果并不像函数的曲线一样平滑,整体出现过拟合的状态。
print(t_x.reshape(-1, 1)) print(model.predict(t_x.reshape(-1, 1)))
输入:预测输入:
[[0.]
[500.]
[1000.]
[1500.]
[2500.]
[6000.]
[8000.]
[10000.]
[12000.]]
print(t_y.reshape(-1,1))
输入:
[[0.] [-1.7345996]
[90.] [90.01992]
[160.] [159.91837]
[210.] [210.06012]
[260.] [260.01797]
[347.5] [347.43182]
[357.5] [357.57867]
[367.5] [367.53287]
[377.5]] [377.4857] 能够看到,比照实在数据神经网络预测的后果是十分准确的,误差比应用多项式拟合的成果好十分多。至此,咱们应用了 4PL、多项式以及神经网络三种可微编程的办法,对弹性阻尼函数进行拟合,迫近实在数据的成果尽管有肯定差别,但总体都拟合的比拟好。同神经网络的办法不同,4PL 和多项式拟合能够间接提供公式和参数,咱们能够不便的将其利用在程序的算法中。而对于神经网络,则须要把神经网络在浏览器里跑起来,上面就介绍一下这个过程。浏览器执行示例 为了可能把神经网络在浏览器里跑起来,并让 JavaScript 能够调用,首先须要将神经网络存储成文件。model.save(‘saved_model/w4model’) 存储到目录后会失去两个文件:keras_metadata.pb 和 saved_model.pb 以及两个目录:assets 和 variables。这里应用的是 TensorFlow 的 tf_saved_model,上面转换模型给 TensorFlow.js 在浏览器应用的时候会用到这个参数。为了可能让模型在浏览器运行,先装置 pip install tensorflowjs 用 tensorflowjs_converter 命令行转换工具进行模型的转换:tensorflowjs_converter –input_format=tf_saved_model \
–output_node_names=”w4model” \
–saved_model_tags=serve ./saved_model/w4model ./web_model 这里 –input_format 参数 tf_saved_model 对应之前模型存储应用的办法,要留神不同办法保留的文件格式和构造的不同互相不兼容。–output_node_names 是模型的名称,–saved_model_tags 中 tag 是用来区别不同的 MetaGraphDef,这是在加载模型所须要的参数其默认值是 serve。通过 tensorflowjs_converter 进行模型转换后,咱们在 web_model 文件夹里会看到 group1-shard1of1.bin 以及 model.json 两个文件。这两个文件中,以 .json 后缀结尾的文件是模型定义文件,以 .bin 后缀结尾的文件是模型的权重文件。您能够把模型定义文件看做 4PL 和多项式的函数,把模型权重文件看做函数的参数。通过 npm 初始化一个 node.js 我的项目,而后在 package.json 配置文件里退出:“dependencies”: {
"@tensorflow/tfjs": "^3.18.0",
"@tensorflow/tfjs-converter": "^3.18.0"
}, 将依赖引入到程序文件中,这里要留神 loadGraphModel 是咱们从 @tensorflow/tfjs-converter 依赖中导入的,尽管 @tensorflow/tfjs 提供 tf.loadGraphModel() 办法加载模型,然而这个办法只实用于 TensorFlow.js 中保留的模型,咱们通过 Python 里 model.save() 办法保留,并用 Converter 转换的模型,必须用 tfjs-converter 依赖包中提供的 loadGraphModel 办法进行加载。
import * as tf from “@tensorflow/tfjs”;
import {loadGraphModel} from “@tensorflow/tfjs-converter”;
window.onload = async () => {
const resultElement = document.getElementById(“result”);
const MODEL_URL = “model.json”;
console.time(“Loading of model”);
const model = await loadGraphModel(MODEL_URL);
console.timeEnd(“Loading of model”);
const test_data = tf.tensor([
[0.0],
[500.0],
[1000.0],
[1500.0],
[2500.0],
[6000.0],
[8000.0],
[10000.0],
[12000.0],
]);
tf.print(test_data);
console.time(“Loading of model”);
let outputs = model.execute(test_data);
console.timeEnd(“execute:”);
tf.print(outputs);
resultElement.innerText = outputs.toString();
}; 这里须要留神的是,因为咱们的模型在预测时应用的是张量作为输出,因而,须要用 tf.tensor() 办法来返回通过包装的张量作为模型的输出。运行程序后,咱们能够从浏览器开发者工具的控制台看到打印的调试信息:
[Violation] ‘load’ handler took 340ms
index.js:12 Loading of model: 67.19482421875 ms
print.ts:34 Tensor
[[0],
[500],
[1000],
[1500],
[2500],
[6000],
[8000],
[10000],
[12000]]
index.js:28 execute: 257.47607421875 ms
print.ts:34 Tensor
[[-1.7345995],
[90.0198822],
[159.9183655],
[210.0600586],
[260.0179443],
[347.4320068],
[357.5788269],
[367.5332947],
[377.4856262]] 咱们会看到控制台输入 webgl 代表 TensorFlow.js 胜利启用了 WebGL 的减速能力,在其减速之下,咱们在预测的时候速度会从 257ms 大幅度晋升到 131ms,屡次预测工夫因为权重和计算图曾经加载到显存中速度会更快,达到 78ms 左右。当然,对于动画来说这种耗时还是有点多,您能够回顾本书端智能的局部,应用相干的工具和办法对模型进行压缩和优化,只须要在模型输入的时候抉择 TensorFlow.js,或者用 TensorFlow 提供的工具把优化后的模型转成 TensorFlow.js 的模型即可复用这个示例中的代码。最初,让咱们再次回到弹性阻尼函数在 JavaScript 里的定义:
damping(x, max) {
let y = Math.abs(x);
y = 0.821 * max / (1 + 4527.779 / Math.pow(y, 1.153));
return Math.round(x < 0 ? -y : y);
}, 把这里的 y 变成模型预测的后果:y = model.execute(tf.tensor([[x],])); 即可用模型来拟合弹性阻尼函数并返回计算结果。您可能会问:既然我有 4PL、多项式拟合等办法,为什么要用神经网络呢?是的,如果您要解决的问题简略到足以用函数拟合的形式解决,同时,您也晓得应该用 4PL 还是多项式异或是其它函数进行拟合来迫近真实情况。满足这两个前提条件,当然能够用函数拟合的形式间接找到对应的函数和参数来解决问题。然而,事实中咱们要解决的问题往往会更加的简单,比方咱们想做个语音输入交互能力,就须要对语音进行音频信号解析、剖析、解决、语义辨认、转文本等等过程,每个环节中要解决的问题都无奈简略用一个函数和单变量多参数形式去形容和拟合变量间的关系。因而,咱们能够把简单的问题用神经网络去拟合,让神经网络通过神经元、Edge、权重、参数等以主动微分的能力去寻找答案。最初,须要指出的是拟合毕竟是一种近似,无奈揭示问题实质是什么?这就是大家常常质疑机器学习可解释性的本源所在。试想一下,如果咱们程序的弹性阻尼算法是依据物理学定义的公式去编写的,那这个算法就具备了可解释性。具备了可解释性,我就可能揭示每个变量的意义以及变量之间的关系,从而使程序和算法具备更准确、粗疏的解决能力。而函数拟合或神经网络拟合的可微编程中,机器学习到了什么?咱们很难给出答案,只能通过重复试验来推定函数或神经网络可能依据输出预计出正确的输入。因而,咱们不能遇到问题就一股脑的应用可微编程去解决,在解释性方面有强诉求的场景中,尽量还是要从问题自身登程找出解决的路径。原文链接:https://click.aliyun.com/m/1000353593/ 本文为阿里云原创内容,未经容许不得转载。