数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

「深度学习福利」大神带你进阶工程师,立即查看>>>
tf.nn.conv2d是TensorFlow里面实现卷积的函数,参考文档对它的介绍并不是很详细,实际上这是搭建卷积神经网络比较核心的一个方法,非常重要
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)
除去name参数用以指定该操作的name,与方法有关的一共五个参数:
第一个参数input:指需要做卷积的输入图像,它要求是一个Tensor,具有[batch, in_height, in_width, in_channels]这样的shape,具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数],注意这是一个4维的Tensor,要求类型为float32和float64其中之一
第二个参数filter:相当于CNN中的卷积核,它要求是一个Tensor,具有[filter_height, filter_width, in_channels, out_channels]这样的shape,具体含义是[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数],要求类型与参数input相同,有一个地方需要注意,第三维in_channels,就是参数input的第四维
第三个参数strides:卷积时在图像每一维的步长,这是一个一维的向量,长度4
第四个参数padding:string类型的量,只能是"SAME","VALID"其中之一,这个值决定了不同的卷积方式(后面会介绍)
第五个参数:use_cudnn_on_gpu:bool类型,是否使用cudnn加速,默认为true
结果返回一个Tensor,这个输出,就是我们常说的feature map
那么TensorFlow的卷积具体是怎样实现的呢,用一些例子去解释它:
1.考虑一种最简单的情况,现在有一张3×3单通道的图像(对应的shape:[1,3,3,1]),用一个1×1的卷积核(对应的shape:[1,1,1,1])去做卷积,最后会得到一张3×3的feature map
2.增加图片的通道数,使用一张3×3五通道的图像(对应的shape:[1,3,3,5]),用一个1×1的卷积核(对应的shape:[1,1,1,1])去做卷积,仍然是一张3×3的feature map,这就相当于每一个像素点,卷积核都与该像素点的每一个通道做点积
input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([1,1,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
3.把卷积核扩大,现在用3×3的卷积核做卷积,最后的输出是一个值,相当于情况2的feature map所有像素点的值求和
input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
4.使用更大的图片将情况2的图片扩大到5×5,仍然是3×3的卷积核,令步长为1,输出3×3的feature map
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
注意我们可以把这种情况看成情况2和情况3的中间状态,卷积核以步长1滑动遍历全图,以下x表示的位置,表示卷积核停留的位置,每停留一个,输出feature map的一个像素
.....
.xxx.
.xxx.
.xxx.
.....
5.上面我们一直令参数padding的值为‘VALID’,当其为‘SAME’时,表示卷积核可以停留在图像边缘,如下,输出5×5的feature map
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
6.如果卷积核有多个
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
此时输出7张5×5的feature map
7.步长不为1的情况,文档里说了对于图片,因为只有两维,通常strides取[1,stride,stride,1]
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')
此时,输出7张3×3的feature map
x.x.x
.....
x.x.x
.....
x.x.x
8.如果batch值不为1,同时输入10张图
input = tf.Variable(tf.random_normal([10,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')
每张图,都有7张3×3的feature map,输出的shape就是[10,3,3,7]
最后,把程序总结一下:
import tensorflow as tf
#case 2
input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([1,1,5,1]))

op2 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
#case 3
input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op3 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
#case 4
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op4 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')
#case 5
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op5 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
#case 6
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op6 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
#case 7
input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op7 = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')
#case 8
input = tf.Variable(tf.random_normal([10,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op8 = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')

init = tf.initialize_all_variables()
with tf.Session() as sess:
sess.run(init)
print("case 2")
print(sess.run(op2))
print("case 3")
print(sess.run(op3))
print("case 4")
print(sess.run(op4))
print("case 5")
print(sess.run(op5))
print("case 6")
print(sess.run(op6))
print("case 7")
print(sess.run(op7))
print("case 8")
print(sess.run(op8))
因为是随机初始化,我的结果是这样的:
case 2
[[[[-0.64064658]
[-1.82183945]
[-2.63191342]]

[[ 8.05008984]
[ 1.66023612]
[ 2.53465152]]

[[-3.51703644]
[-5.92647743]
[ 0.55595356]]]]
case 3
[[[[ 10.53139973]]]]
case 4
[[[[ 10.45460224]
[ 6.23760509]
[ 4.97157574]]

[[ 3.05653667]
[-11.43907833]
[ -2.05077457]]

[[ -7.48340607]
[ -0.90697062]
[ 3.27171206]]]]
case 5
[[[[ 5.30279875]
[ -2.75329947]
[ 5.62432575]
[-10.24609661]
[ 0.12603235]]

[[ 0.2113893 ]
[ 1.73748684]
[ -3.04372549]
[ -7.2625494 ]
[-12.76445198]]

[[ -1.57414591]
[ -3.39802694]
[ -6.01582575]
[ -1.73042905]
[ -3.07183361]]

[[ 1.41795194]
[ -2.02815866]
[-17.08983231]
[ 11.98958111]
[ 2.44879103]]

[[ 0.29902667]
[ -3.19712877]
[ -2.84978414]
[ -2.71143317]
[ 5.99366283]]]]
case 6
[[[[ 12.02504349 4.35077286 2.67207813 5.77893162 6.98221684
-0.96858567 -8.1147871 ]
[ -0.02988982 -2.52141953 15.24755192 6.39476395 -4.36355495
-2.34515095 5.55743504]
[ -2.74448752 -1.62703776 -6.84849405 10.12248802 3.7408421
4.71439075 6.13722801]
[ 0.82365227 -1.00546622 -3.29460764 5.12690163 -0.75699937
-2.60097408 -8.33882809]
[ 0.76171923 -0.86230004 -6.30558443 -5.58426857 2.70478535
8.98232937 -2.45504045]]

[[ 3.13419819 -13.96483231 0.42031103 2.97559547 6.86646557
-3.44916964 -0.10199898]
[ 11.65359879 -5.2145977 4.28352737 2.68335319 3.21993709
-6.77338028 8.08918095]
[ 0.91533852 -0.31835344 -1.06122255 -9.11237717 5.05267143
5.6913228 -5.23855162]
[ -0.58775592 -5.03531456 14.70254898 9.78966522 -11.00562763
-4.08925819 -3.29650426]
[ -2.23447251 -0.18028721 -4.80610704 11.2093544 -6.72472
-2.67547607 1.68422937]]

[[ -3.40548897 -9.70355129 -1.05640507 -2.55293012 -2.78455877
-15.05377483 -4.16571808]
[ 13.66925812 2.87588191 8.29056358 6.71941566 2.56558466
10.10329056 2.88392687]
[ -6.30473804 -3.3073864 12.43273926 -0.66088223 2.94875336
0.06056046 -2.78857946]
[ -7.14735603 -1.44281793 3.3629775 -7.87305021 2.00383091
-2.50426936 -6.93097973]
[ -3.15817571 1.85821593 0.60049552 -0.43315536 -4.43284273
0.54264796 1.54882073]]

[[ 2.19440389 -0.21308756 -4.35629082 -3.62100363 -0.08513772
-0.80940366 7.57606506]
[ -2.65713739 0.45524287 -16.04298019 -5.19629049 -0.63200498
1.13256514 -6.70045137]
[ 8.00792599 4.09538221 -6.16250181 8.35843849 -4.25959206
-1.5945878 -7.60996151]
[ 8.56787586 5.85663748 -4.38656425 0.12728286 -6.53928804
2.3200655 9.47253895]
[ -6.62967777 2.88872099 -2.76913023 -0.86287498 -1.4262073
-6.59967232 5.97229099]]

[[ -3.59423327 4.60458899 -5.08300591 1.32078576 3.27156973
0.5302844 -5.27635145]
[ -0.87793881 1.79624665 1.66793108 -4.70763969 -2.87593603
-1.26820421 -7.72825718]
[ -1.49699068 -3.40959787 -1.21225107 -1.11641395 -8.50123024
-0.59399474 3.18010235]
[ -4.4249506 -0.73349547 -1.49064219 -6.09967899 5.18624878
-3.80284953 -0.55285597]
[ -1.42934585 2.76053572 -5.19795799 0.83952439 -0.15203482
0.28564462 2.66513705]]]]
case 7
[[[[ 2.66223097 2.64498258 -2.93302107 3.50935125 4.62247562
2.04241085 -2.65325522]
[ -0.03272867 -1.00103927 -4.3691597 2.16724801 7.75251007
-4.6788125 -0.89318085]
[ 4.74175072 -0.80443329 -1.02710629 -6.68772554 4.57605314
-3.72993755 4.79951382]]

[[ 5.249547 8.92288399 7.10703182 -9.10498428 -7.43814278
-8.69616318 1.78862095]
[ 7.53669024 -14.52316284 -2.55870199 -1.11976743 3.81035042
2.45559502 -2.35436153]
[ 3.93275881 5.11939669 -4.7114296 -11.96386623 2.11866689
0.57433248 -7.19815397]]

[[ 0.25111672 1.40801668 1.28818977 -2.64093828 0.98182392
3.69512987 4.78833389]
[ 0.30391204 -10.26406097 6.05877018 -6.04775047 8.95922089
0.80235004 -5.4520669 ]
[ -7.24697018 -2.33498096 -10.20039558 -1.24307609 3.99351597
-8.1029129 2.44411373]]]]
case 8
[[[[ -6.84037447e+00 1.33321762e-01 -5.09891272e+00 5.55682087e+00
8.22002888e+00 -4.94586229e-02 4.19012117e+00]
[ 6.79884481e+00 1.21652853e+00 -5.69557810e+00 -1.33555794e+00
3.24849486e-01 4.88868570e+00 -3.90220714e+00]
[ -3.53190374e+00 -4.11765718e+00 4.54340839e+00 1.85549557e+00
-3.38682461e+00 2.62719369e+00 -4.98658371e+00]]

[[ -9.86354351e+00 -6.76713943e+00 3.62617874e+00 -6.16720629e+00
1.96754158e+00 -4.54203081e+00 -1.37485743e+00]
[ -1.76783955e+00 2.35163045e+00 -2.21175838e+00 3.83091879e+00
3.16964531e+00 -7.58307219e+00 4.71943617e+00]
[ 1.20776439e+00 4.86006308e+00 1.04233503e+01 -7.82327271e+00
5.39195156e+00 -6.31672382e+00 1.35577369e+00]]

[[ -3.65947580e+00 -1.98961139e+00 7.53771305e+00 2.79224634e-01
-2.90050888e+00 -3.57466817e+00 -6.33232594e-01]
[ 5.89931488e-01 2.83219159e-01 -1.65850735e+00 -6.45545387e+00
-1.17044592e+00 1.40343285e+00 5.74970901e-01]
[ -8.58810043e+00 -1.25172977e+01 6.84177876e-01 3.80004168e+00
-1.54420209e+00 -3.32161427e+00 -1.05423713e+00]]]


[[[ -4.82677078e+00 3.11167526e+00 -4.32694483e+00 -4.77198696e+00
2.32186103e+00 1.65402293e-01 -5.32707453e+00]
[ 3.91779566e+00 6.27949667e+00 2.32975650e+00 -1.06336937e+01
4.44044876e+00 8.08288479e+00 -5.83346319e+00]
[ -2.82141399e+00 -9.16103745e+00 6.98908520e+00 -5.66505909e+00
-2.11039782e+00 2.27499461e+00 -5.74120235e+00]]

[[ 6.71680808e-01 -4.01104212e+00 -4.61760712e+00 1.02667952e+01
-8.21200657e+00 -8.57054043e+00 1.71461976e+00]
[ 2.40794683e+00 -2.63071585e+00 9.68963623e+00 -4.51778412e+00
-3.91073084e+00 -5.91874409e+00 9.96273613e+00]
[ 2.67705870e+00 2.85607010e-01 2.45853162e+00 4.44810390e+00
-2.11300468e+00 -5.77583075e+00 2.83322239e+00]]

[[ -8.21949577e+00 -7.57754421e+00 3.93484974e+00 2.26189137e+00
-3.49395227e+00 -6.40283823e+00 -6.00450039e-01]
[ 2.95964479e-02 -1.19976890e+00 5.38537979e+00 4.62369967e+00
3.89780998e+00 -6.36872959e+00 7.12107182e+00]
[ -8.85006547e-01 1.92706418e+00 3.26668215e+00 2.03566647e+00
1.44209075e+00 -6.48463774e+00 -8.33671093e-02]]]


[[[ -2.64583921e+00 3.86011934e+00 4.18198538e+00 3.50338411e+00
6.35944796e+00 -4.28423309e+00 4.87355423e+00]
[ 4.42271233e+00 3.92883778e+00 -5.59371090e+00 4.98251200e+00
-3.45068884e+00 2.91921115e+00 1.03779554e+00]
[ 1.36162388e+00 -1.06808968e+01 -3.92534947e+00 1.85111761e-01
-4.87255526e+00 1.66666222e+01 -1.04918976e+01]]

[[ -4.34632540e+00 1.74614882e+00 -2.89012527e+00 -8.74067783e+00
5.06610107e+00 1.24989772e+00 -3.06433105e+00]
[ 2.49973416e+00 2.14041996e+00 -4.71008825e+00 7.39326143e+00
3.94770741e+00 8.23049164e+00 -1.67046225e+00]
[ -2.94665837e+00 -4.58543825e+00 7.21219683e+00 1.09780006e+01
5.17258358e+00 7.90257788e+00 -2.13929534e+00]]

[[ 4.20402241e+00 -2.98926830e+00 -3.89006615e-01 -8.16001511e+00
-2.38355541e+00 1.42584383e+00 -5.46632290e+00]
[ 5.52395058e+00 5.09255171e+00 -1.08742390e+01 -4.96262169e+00
-1.35298109e+00 3.65663052e-01 -3.40589857e+00]
[ -6.95647061e-01 -4.12855625e+00 2.66609401e-01 -9.39565372e+00
-3.85058141e+00 2.51248240e-01 -5.77149725e+00]]]


[[[ 1.22103825e+01 5.72040796e+00 -3.56989503e+00 -1.02248180e+00
-5.20942688e-01 7.15008640e+00 3.43482435e-01]
[ 6.01409674e+00 -1.59511256e+00 -6.48080063e+00 -1.82889538e+01
-1.03537569e+01 -1.48270035e+01 -5.26662111e+00]
[ 5.51758146e+00 -2.91831636e+00 3.75461340e-01 -9.23893452e-02
-9.22101116e+00 7.16952372e+00 -6.86479330e-01]]

[[ -3.03645611e+00 6.68620300e+00 -3.31973934e+00 -4.91346550e+00
9.20719814e+00 -2.55552864e+00 -2.16087699e-02]
[ -3.02986956e+00 -1.29726543e+01 1.53023469e+00 -8.19733238e+00
5.68085670e+00 -1.72856820e+00 -4.69369221e+00]
[ -6.67176056e+00 8.76355553e+00 2.18996063e-01 -4.38777208e+00
-6.35764122e-01 -1.37812555e+00 -4.41474581e+00]]

[[ 2.25345469e+00 1.02142305e+01 -1.71714854e+00 -5.29060185e-01
2.27982092e+00 -8.75302982e+00 7.13998675e-02]
[ -6.67547846e+00 3.67722750e+00 -3.44172812e+00 5.69674826e+00
-2.28723526e+00 5.92991543e+00 5.53608060e-01]
[ -1.01174891e-01 -2.73731589e+00 -4.06187654e-01 6.54158068e+00
2.59603882e+00 2.99202776e+00 -2.22350287e+00]]]


[[[ -1.81271315e+00 2.47674489e+00 -2.90284491e+00 1.34291325e+01
7.69864845e+00 -1.27134466e+00 3.02233839e+00]
[ -2.08135307e-01 1.03206539e+00 1.90775347e+00 9.01517391e+00
-3.52140331e+00 9.05393791e+00 -9.12732124e-01]
[ 1.12128162e+00 5.98179293e+00 -2.27206993e+00 -5.21281779e-01
6.20835352e+00 3.73474598e+00 1.18961644e+00]]

[[ 3.17242837e+00 -6.00571585e+00 2.37661076e+00 -5.64483738e+00
-6.45412731e+00 8.75251675e+00 7.33790398e-02]
[ 3.08957529e+00 -1.06855690e-01 -5.16810894e-01 -9.41085911e+00
8.23878098e+00 6.79738426e+00 -1.23478663e+00]
[ -9.20640087e+00 -6.82801771e+00 -5.96975613e+00 7.61030674e-01
-4.35995817e+00 -3.54818010e+00 -2.56281614e+00]]

[[ 4.69872713e-01 8.36402321e+00 5.37103415e-01 -1.68033957e-01
-3.21731424e+00 -7.34270859e+00 -3.14253521e+00]
[ 6.69656086e+00 -5.27954197e+00 -8.57314682e+00 4.84328842e+00
-2.96387672e+00 2.47114658e+00 2.85376692e+00]
[ -7.86032295e+00 -7.18845367e+00 -3.27161223e-01 9.27330971e+00
-6.14093494e+00 -4.49041557e+00 3.47160912e+00]]]


[[[ -1.89188433e+00 5.43082857e+00 6.04252160e-01 6.92894220e+00
8.59178162e+00 1.02003086e+00 5.31300211e+00]
[ -8.97491455e-01 6.52438164e+00 -4.43710327e+00 7.10509634e+00
8.84234428e+00 3.08552694e+00 2.78152227e+00]
[ -9.40537453e-02 2.34666920e+00 -5.57496691e+00 -8.62346458e+00
-1.32807600e+00 -8.12027454e-02 -9.00946975e-01]]

[[ -3.53673506e+00 8.93675327e+00 3.27456236e-01 -3.41519475e+00
7.69804525e+00 -5.18698692e+00 -3.96991730e+00]
[ 1.99988627e+00 -9.16149998e+00 -7.49944544e+00 5.02162695e-01
3.57059622e+00 9.17566013e+00 -1.77589107e+00]
[ -1.18147678e+01 -7.68992901e+00 1.88449645e+00 2.77643538e+00
-1.11342735e+01 -3.12916255e+00 -3.34161663e+00]]

[[ -3.62668943e+00 -3.10993242e+00 3.60834384e+00 4.69678783e+00
-1.73794723e+00 -1.27035933e+01 3.65882218e-01]
[ -8.97550106e+00 -4.33533072e-01 4.41743970e-01 -5.83433771e+00
-4.85818958e+00 9.56629372e+00 3.56375504e+00]
[ -6.87092066e+00 1.96412420e+00 5.14182663e+00 -8.97769547e+00
3.61136627e+00 5.91387987e-01 -2.95224571e+00]]]


[[[ -1.11802626e+00 3.24175072e+00 5.94067669e+00 9.29727936e+00
9.28199863e+00 -4.80889034e+00 6.96202660e+00]
[ 7.23959684e+00 3.11182523e+00 1.84116721e+00 5.12095928e-01
-7.65049171e+00 -4.05325556e+00 5.38544941e+00]
[ 4.66621685e+00 -1.61665392e+00 9.76448345e+00 2.38519001e+00
-2.06760812e+00 -6.03633642e-01 3.66192675e+00]]

[[ 1.52149725e+00 -1.84441996e+00 4.87877655e+00 2.96750760e+00
2.37311172e+00 -2.98487616e+00 9.98114228e-01]
[ 9.20035839e+00 5.24396753e+00 -2.57312679e+00 -7.26040459e+00
-1.17509928e+01 6.85688591e+00 3.37383580e+00]
[ 6.17629957e+00 -5.15294194e-01 -1.64212489e+00 -5.70274448e+00
-2.36294913e+00 2.60432816e+00 2.63957453e+00]]

[[ 7.91168213e-03 -1.15018034e+00 3.05471039e+00 3.31086922e+00
5.35744762e+00 1.14832592e+00 9.56500292e-01]
[ 4.86464739e+00 5.37348413e+00 1.42920148e+00 1.62809372e+00
2.61656570e+00 7.88479471e+00 -6.09324336e-01]
[ 7.71319962e+00 -1.73930550e+00 -2.99925613e+00 -3.14857435e+00
3.19194889e+00 1.70928288e+00 4.90955710e-01]]]


[[[ -1.79046512e+00 8.54369068e+00 1.85044312e+00 -9.88471413e+00
9.52995300e-01 -1.34820042e+01 -1.13713551e+01]
[ 8.37582207e+00 6.64692163e+00 -3.22429276e+00 3.37997460e+00
3.91468263e+00 6.96061993e+00 -1.18029404e+00]
[ -2.13278866e+00 4.36152029e+00 -4.14593410e+00 -2.15160155e+00
1.90767622e+00 1.16321917e+01 -3.72644544e+00]]

[[ -5.03508925e-01 -6.33426476e+00 -1.06393566e+01 -6.49301624e+00
-6.31036520e+00 3.13485146e+00 -5.77433109e-01]
[ 7.41444230e-01 -4.87326956e+00 -5.98253345e+00 -9.14121056e+00
-8.64077091e-01 2.06696177e+00 -7.59688473e+00]
[ 1.38767815e+00 1.84418947e-01 5.72539902e+00 -2.07557893e+00
9.70911503e-01 1.16765432e+01 -1.40111232e+00]]

[[ -1.21869087e+00 2.44499159e+00 -1.65706706e+00 -6.19807529e+00
-5.56950712e+00 -1.72372568e+00 3.62687564e+00]
[ 2.23708963e+00 -2.87862611e+00 2.71666467e-01 4.35115099e+00
-8.85548592e-01 2.91860628e+00 8.10848951e-01]
[ -5.33635712e+00 7.15072036e-01 5.21240902e+00 -3.11152220e+00
2.01623154e+00 -2.28398323e-01 -3.23233747e+00]]]


[[[ 3.77991509e+00 5.53513861e+00 -1.82022047e+00 4.22430277e+00
5.60331726e+00 -4.28308249e+00 4.54524136e+00]
[ -5.30983162e+00 -3.45605731e+00 2.69374561e+00 -6.16836596e+00
-9.18601036e+00 -1.58697796e+00 -5.73809910e+00]
[ 2.18868661e+00 6.96338892e-01 1.88057957e+01 -4.21353197e+00
1.20818818e+00 2.85108542e+00 6.62180042e+00]]

[[ 1.01285219e+01 -4.86819077e+00 -2.45067930e+00 7.50106812e-01
4.37201977e+00 4.78472042e+00 1.19103444e+00]
[ -3.26395583e+00 -5.59358537e-01 1.52001972e+01 -5.93994498e-01
-1.49040818e+00 -7.02547312e+00 -1.29268813e+00]
[ 1.02763653e+01 1.31108007e+01 -2.91605043e+00 -1.37688947e+00
3.33029580e+00 1.96966705e+01 2.55259371e+00]]

[[ 4.58397627e+00 -3.19160700e+00 -6.51985502e+00 1.02908373e+01
-4.17618275e+00 -9.69347239e-01 7.46259832e+00]
[ 6.09876537e+00 1.33044279e+00 5.04027081e+00 -6.87740147e-01
4.14770365e+00 -2.26751328e-01 1.54876924e+00]
[ 2.70127630e+00 -1.59834003e+00 -1.82587504e+00 -5.92888784e+00
-5.65038967e+00 -6.46078014e+00 -1.80765367e+00]]]


[[[ -1.57899165e+00 3.39969063e+00 1.02308102e+01 -7.77082300e+00
-8.02129686e-01 -3.67387819e+00 -1.37204361e+00]
[ 3.93093729e+00 6.17498016e+00 -1.41695750e+00 -1.26903206e-01
2.18985319e+00 5.83657503e-01 7.39725351e-01]
[ 5.53898287e+00 2.22283316e+00 -1.10478985e+00 2.68644023e+00
-2.59913635e+00 3.74231935e+00 4.85016155e+00]]

[[ 4.05368614e+00 -3.74058294e+00 7.32348633e+00 -1.17656231e+00
3.71810269e+00 -1.63957381e+00 9.91670132e-01]
[ -1.29317007e+01 1.12296543e+01 -1.13844347e+01 -7.13933802e+00
-8.65884399e+00 -5.56065178e+00 -1.46718264e+00]
[ -8.08718109e+00 -1.98826480e+00 -4.07488203e+00 2.06440473e+00
1.13524094e+01 5.68703651e+00 -2.18706942e+00]]

[[ 1.51166654e+00 -6.84034204e+00 9.33474350e+00 -4.80931902e+00
-6.24172688e-02 -4.21381521e+00 -5.73313046e+00]
[ -1.35943902e+00 5.27799511e+00 -3.77813816e+00 6.88291168e+00
4.35068893e+00 -1.02540245e+01 8.86861205e-01]
[ -4.49999619e+00 -2.97630525e+00 -6.18604183e-01 -2.49702692e+00
-6.76169348e+00 -2.55930996e+00 -2.71291423e+00]]]]
---------------------
作者:xf__mao
来源:CSDN
原文:https://blog.csdn.net/mao_xiao_feng/article/details/53444333
版权声明:本文为博主原创文章,转载请附上博文链接!
人工智能
2018-11-07 10:19:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
词图
词图指的是句子中所有词可能构成的图。如果一个词A的下一个词可能是B的话,那么A和B之间具有一条路径E(A,B)。一个词可能有多个后续,同时也可能有多个前驱,它们构成的图我称作词图。
需要稀疏2维矩阵模型,以一个词的起始位置作为行,终止位置作为列,可以得到一个二维矩阵。例如:“他说的确实在理”这句话
图词的存储方法: 一种是的DynamicArray法,一种是快速offset法。Hanlp代码中采用的是第二种方法。
1、DynamicArray(二维数组)法
在词图中,行和列的关系:col为n 的列中所有词可以与row为n 的所有行中的词进行组合。例如“的确”这个词,它的col =5,需要和它计算平滑值的有两个,分别是row =5的两个词:“实”和“实在”。但是在遍历和插入的时候,需要一个个比较col和row的关系,复杂度是O(N)。
2、快速offset
一个一维数组,每个元素是一个单链表
“的确”的行号是4,长度是2,4+2=6,于是第六行的两个词“实/实在”就是“的确”的后续。
同时这种方法速度非常快,插入和查询的时间都是O(1)。

Hanlp核心词典:
最短路径算法—viterbi(动态规划路径)

Frequency:核心词典中的词频
nTwoWordsFreq:共现词频
intMAX_FREQUENCY= 25146057
double dTemp =(double) 1 / MAX_FREQUENCY +0.00001
dSmoothingPara =0.1
Viterbi最短路径有向图
1、计算过程从上至下,根据计算出的权重值变更前驱结点,保证前驱结点唯一(动态规划路径)
2、计算结束后,从最后一个结点开始取出term,依次取出该结点的前驱结点即可分词结果:理,在,确实,的,说,他

文章来源于亚当-adam的博客
人工智能
2018-11-07 10:19:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
【一】词性标注
词性标注分为2部分,首先是分词,然后基于分词结果做词性标注。
【二】jieba的词性标注代码流程详解
1. 代码位置
jieba/posseg/_init_.py
2. 流程分析 def cut(sentence, HMM=True): """ Global `cut` function that supports parallel processing. Note that this only works using dt, custom POSTokenizer instances are not supported. """ global dt # 该pool 默认为None if jieba.pool is None: # 调用POSTokenizer的cut接口进行词性标注 for w in dt.cut(sentence, HMM=HMM): yield w else: parts = strdecode(sentence).splitlines(True) if HMM: result = jieba.pool.map(_lcut_internal, parts) else: result = jieba.pool.map(_lcut_internal_no_hmm, parts) for r in result: for w in r: yield w
可以看出,对于未登录词,采用HMM模型进行分词与词性标注。 def __cut_internal(self, sentence, HMM=True): # 词典加载 self.makesure_userdict_loaded() sentence = strdecode(sentence) # 中文正则表达式匹配 blocks = re_han_internal.split(sentence) # 设置分词函数 if HMM: cut_blk = self.__cut_DAG else: cut_blk = self.__cut_DAG_NO_HMM for blk in blocks: # 如果是中文,则调用分词接口进行分词与词性标注 if re_han_internal.match(blk): for word in cut_blk(blk): yield word else: tmp = re_skip_internal.split(blk) for x in tmp: if re_skip_internal.match(x): yield pair(x, 'x') else: for xx in x: # 如果是数字,则使用m标注词性,由于number的n和u已经用于其他词性,因此使用m if re_num.match(xx): yield pair(xx, 'm') # 如果是英文,标注为eng elif re_eng.match(x): yield pair(xx, 'eng') # 否则,一律标注为x else: yield pair(xx, 'x') # 对于未位登录词,采用HMM模型进行词性标注 def __cut_detail(self, sentence): blocks = re_han_detail.split(sentence) for blk in blocks: if re_han_detail.match(blk): # 使用HMM模型进行词性标注 for word in self.__cut(blk): yield word else: tmp = re_skip_detail.split(blk) for x in tmp: if x: if re_num.match(x): yield pair(x, 'm') elif re_eng.match(x): yield pair(x, 'eng') else: yield pair(x, 'x') def __cut(self, sentence): # 使用viterbi算法进行状态序列求解,这里的状态序列包含2部分 # 一是:词的位置,而是词性。由于一词多性,因此需要计算出该词概率最大的词性 prob, pos_list = viterbi( sentence, char_state_tab_P, start_P, trans_P, emit_P) begin, nexti = 0, 0 for i, char in enumerate(sentence): pos = pos_list[i][0] if pos == 'B': begin = i elif pos == 'E': yield pair(sentence[begin:i + 1], pos_list[i][1]) nexti = i + 1 elif pos == 'S': yield pair(char, pos_list[i][1]) nexti = i + 1 if nexti < len(sentence): yield pair(sentence[nexti:], pos_list[nexti][1])
这里,依旧使用viterbi算法进行状态序列求解。这里就不分析了,算法流程和前面的未登录词的分词一致。只是强调一点,这里的状态序列包含两部分:一是:字的位置,即BMES,而是词对应的词性,如 :n,a等

【三】总结
jieba总体而言,包含如下三个功能:分词、词性标注、关键字提取。使用的都是传统的方法,如基于词典前缀的匹配、基于HMM模型对未登录词进行分割与词性标注、基于TF-IDF和TextRank进行关键字提取。这三大功能,全都离不开jieba基于语料库统计出来的词典,包括词频、词性、HMM模型参数(状态转移概率矩阵、发射概率矩阵、初始状态概率向量)。
要想使用jieba得到好的分词效果,需要替换自己的词典,训练自己的HMM参数,而这些,基本都是基于语料库统计得到的。
人工智能
2018-11-06 22:19:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
腾讯人工智能AI开放平台上提供了很多免费的人工智能API,开发人员只需要一个QQ号就可以登录进去使用。
腾讯人工智能AI开放平台的地址: https://ai.qq.com/

里面的好东西很多,以自然语言处理的人工智能API为例。
假设我们有一个句子:腾讯AI人工智能开放平台。我们希望用腾讯的人工智能开放平台里提供的自然语言处理API对这个句子进行智能分词。
用您的QQ号登录腾讯人工智能开放平台,创建一个新的应用:
https://ai.qq.com/
根据您的实际需要选择自然语言处理的具体类别: 文本朗读(Text to speech)/语音合成(Speech synthesis) 语音识别(Speech recognition) 中文自动分词(Chinese word segmentation) 词性标注(Part-of-speech tagging) 句法分析(Parsing) 自然语言生成(Natural language generation) 文本分类(Text categorization) 信息检索(Information retrieval) 信息抽取(Information extraction) 文字校对(Text-proofing) 问答系统(Question answering) 机器翻译(Machine translation) 自动摘要(Automatic summarization) 文字蕴涵(Textual entailment)

创建应用之后生成的app id和app key要记下来,在代码里要使用。
新建一个js文件,输入如下代码: var md5 = require('md5'); var app_id = "2107823355"; var time_stamp = Date.now() / 1000; var nonce_str = Date.now(); var text = "腾讯AI人工智能开放平台"; var app_key = "LHGNH0usjUTRRRSA"; var input = "app_id=" + app_id + "&nonce_str=" + nonce_str + "&text=" + encodeURI(text) + "&time_stamp=" + time_stamp + "&app_key=" + app_key; var upper = md5(input).toUpperCase(); console.log(upper); input = input + "&sign=" + upper; var request = require('request'); var oOptions = { url: "https://api.ai.qq.com/fcgi-bin/nlp/nlp_wordseg", method: "POST", headers: { "content-type": "application/x-www-form-urlencoded", }, body: input }; console.log("request sent: " + oOptions.body); var action = new Promise(function(resolve,reject){ request(oOptions,function(error,response,body){ console.log("response: " + body); }); // end of request });
通过nodejs里的request组件, 使用HTTP POST调用 https://api.ai.qq.com/fcgi-bin/nlp/nlp_wordseg去消费腾讯人工智能开放平台的自然语言处理的分词API:
这些代码的详细解释,我已经在我之前的NLP版本里介绍过了:
使用命令行 node nlp.js即可消费该API并查看结果:
要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:
人工智能
2018-10-23 14:41:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
最近无意发现腾讯AI开放平台上提供了大量好玩的人工智能云服务,而且是完全免费的。只需要用QQ号登录即可。这么好的东西,作为一个程序员,当然要试试了!
从上图可以看出腾讯AI开放平台提供的人工智能服务主要有自然语言处理,计算机视觉和智能语音三大类。我当然是从最简单的自然语言处理开始学习。
这是腾讯AI开放平台的自然语言处理的API说明:
https://ai.qq.com/doc/nlpbase.shtml
其实也就是我们熟悉的Restful API调用:
新建一个PHP文件,把下面这个链接的源代码粘贴进去:
https://github.com/i042416/wechat/blob/master/tencent/newfile.php
然后在Eclipse里Run As->PHP CLI Application,
即可看到下列输出:“腾讯AI开放平台”这个句子,已经被腾讯人工智能成功分词成了:腾讯,AI,开放,平台。
下面就来解释下PHP代码的主要逻辑。 $appkey = 'LHGNH0usjUTRRRSA'; $params = array( 'app_id' => '2107823355', 'time_stamp' => strval(time()), 'nonce_str' => strval(rand()), 'text' => '腾讯AI开放平台', 'sign' => '', );
第一行和第四行分别为我在腾讯AI平台上创建的测试应用的key和ID。第五行是发起请求的时间戳,第六行用rand()生成了一个随机数,第七行为希望使用腾讯AI人工智能分词的输入句子,第8行sign是一个需要动态计算出的签名值。
这个签名值在下列PHP函数调用里计算:
$params['sign'] = getReqSign($params, $appkey);
现在进入getRegSign内部:
首先执行ksort对PHP的key-value数组$params按照key进行升序排序,可以比较上图升序排序前和下图排序后的顺序:
接着把字典里每个key-value键值对用key=value&key=value...这种形式连接起来,注意text值需要用urlencode进行编码。
最后把连接好的字符串用md5函数计算出MD5哈希值,然后用strtoupper转成大写,这就是计算好的签名值。
最后一步就是调用doHttpPost发送请求。
Post请求的fields仍然是key=value&key=value这种类型的键值对,参考下面调试器里的截图:
为方便您的参考,我把调试器里显示的$body记录在下面:
app_id=2107823355&time_stamp=1533960023&nonce_str=12169&text=%CC%DA%D1%B6AI%BF%AA%B7%C5%C6%BD%CC%A8&sign=EA9CAC254A17729B20FAF28757E775DD
最后发送请求,得到结果: curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_NOBODY, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, true); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec($curl); if ($response === false) { $response = false; break; } $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ($code != 200) { $response = false; break; }

要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:
人工智能
2018-10-23 14:41:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
如今,我们生活在一个信息爆炸的时代,难免会受到网络上虚假信息的干扰。但大多数时候,网民并不是假新闻的直接制造者,而更多是扮演传播者的角色。但传播者的影响力也不容小觑,胡乱散播消息也会造成严重的后果。比如,2016年美国大选期间虚假新闻的传播被指左右了民意,影响了大选结果。
事实上,假新闻的历史与书面文字的历史一样古老,甚至比其更悠久。只不过技术的进步“成就”了我们现在所知道的各种类型的假新闻版本甚至传播方式。现在的问题是,既然技术能催生假新闻的猖獗,那么新兴技术,特别是人工智能,是否也能成为防止假新闻泛滥的最有效的工具呢?
从去年开始,虚假新闻问题因为跟美国总统大选牵连在一起而受到越来越多人的关注。具体来说,在一次美国总统大选期间,民主党和共和党都曾利用互联网和社交媒体来散布错误的信息,虽然这是一种比较阴险的招数,但我们不得不承认,散布虚假新闻这一做法见效快而且几乎没有太大的成本,毕竟任何组织和个人都可以随便在各大新闻网站以及社交媒体和博客上发布新闻,而对网民来说,他们很难辨别这些信息的真假,因此很容易受到影响。
在这种情况下, 为了杜绝虚假新闻的蔓延,很多公司开始尝试用人工智能技术来解决这个问题 。像Facebook和谷歌这样的科技巨头,已经开始使用这项技术来识别那些看起来似是而非的内容,当然,很快我们也会看到其他媒体公司、政府以及其他关心虚假新闻的组织使用类似的工具去实现同一个目的。虽然从目前来说人工智能并不是一个最完美的解决方案,但或许在一定程度上可以减轻这个问题。
人工智能
2018-10-23 13:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
关于机器学习这个话题,我相信我这个公众号1500多位关注者里,一定有很多朋友的水平比Jerry高得多。如果您看过我以前两篇文章,您就会发现,我对机器学习仅仅停留在会使用API的层面上。 使用Java程序消费SAP Leonardo的机器学习API 使用Recast.AI创建具有人工智能的聊天机器人
关于机器学习在SAP标准产品中的应用,Jerry只知道一个例子,就是机器学习在SAP Cloud for Customer(以下简称C4C)中的应用。今天Jerry就把这个例子分享给大家。如果除了C4C外,您还知道SAP其他产品也已经启用了机器学习,请留言,让大家也多增长一些见识,谢谢。
SAP C4C在1708这个版本最先引入机器学习的支持,用于销售场景中的Deal Intelligence(智能交易)和服务场景中的Automatic Ticket Classification(Ticket智能分类)。根据SAP官方网站上发布的信息,到C4C 1802为止,SAP C4C销售和服务领域内支持的机器学习场景如下: Deal Intelligence Lead Intelligence Account Intelligence Ticket Intelligence
本文Jerry将选择三个我熟悉的场景分享给大家。
文章目录 C4C系统启用机器学习的前提条件 C4C系统启用机器学习的主要步骤 机器学习在C4C客户管理场景中的应用 机器学习在C4C销售商机管理中的应用 机器学习在C4C销售报价单的产品推荐场景中的作用
C4C系统启用机器学习的前提条件
C4C机器学习的思路是分析系统内已有的历史数据,以进行模式识别,创建统计模型对将来的业务决策做出预测。因此历史数据成为C4C机器学习场景一个至关重要的输入条件。
SAP C4C机器学习对于历史数据规模的要求是:对于相关场景至少存在过去12个月的数据,数量不得少于5000个,并且必须满足SAP帮助文档上定义的特征分布。
C4C系统启用机器学习的主要步骤
C4C机器学习功能在每个tenant上默认处于关闭状态。希望启用机器学习的客户需要向SAP提交一个Incident,按照SAP提供的一个模板填写需要启用机器学习的具体场景。作为一个SaaS解决方案,绝大多数复杂的机器学习启用步骤都由SAP工作人员完成,剩下需要由C4C客户在C4C tenant上完成的步骤仅仅是在C4C工作中心视图Predication Services里进行的简单配置工作。
点击Model Setup超链接进行机器学习的模型配置:
注意图中的"Readiness"这一列,代表当前tenant上相关的历史数据的规模和分布是否足以满足SAP定义的创建机器学习训练模型的条件。
如果条件不满足,点击"View Report"能看到具体是历史数据的哪个维度不满足:
历史数据准备好之后,通过点击下图Model表格的工具栏上的按钮"Add Model"创建机器学习的模型,训练并激活模型,然后就能在C4C的业务场景中使用机器学习提供的强大功能了。
这些按钮背后的技术细节全部被SAP封装好,确保客户的相关人员即使没有任何机器学习的技术背景,也能在C4C系统上快速启用机器学习的功能。
我们注意到上图有一列"Data Source", 代表该场景需要的模型是否支持以外部文件的方式将历史数据导入系统。"Auto Extraction"则代表直接使用当前tenant的数据作为历史数据。
等模型训练结束后状态变为Active,就可以开始在C4C业务场景中使用机器学习了。
机器学习在C4C客户管理场景中的应用
使用机器学习进行客户管理,我们可以得到客户360度全方位的视图。
打开SAP C4C的客户工作中心,在客户列表里选中任意一个客户进入明细页面,能在右边看到一个名为Insights的区域。
这些客户的360度视图是基于C4C内部和外部的数据源分析得出的,有助于销售人员进行更有针对性的客户计划和销售。C4C的外部数据源采用的是第三方数据提供商Bombora。
通过Insights面板,我们能够获得通过机器学习得出的每个客户的购买倾向的分数,并且能看出就我们关注的某一话题,该客户的行为和倾向到底如何。Bombora会从该客户相关的B2B网站上捕捉能够反映该客户购买倾向的各种线索。当检测到客户在某个话题上的线索数量有明显增加时,我们称这个客户就该话题表现出了一个Surge(抱歉,Jerry实在不知道这个单词如何翻译成中文)。我们会给出Surge的分数,范围在1到99之间,每周更新一次。
SAP C4C会将某个客户总的Surge分数显示在屏幕右侧Insights面板内,同时显示出Surge分数最高的前三个话题。下图Surge分数前三的话题依次为:Artificial Intelligence, Machine Learning和Collaboration Software。
在C4C工作中心视图Predication Services的Third Party Data可以对Insights面板里需要关注的话题进行配置:

机器学习在C4C销售商机管理中的应用
在销售商机(Opportunity)列表里选中某个商机,能看到右边会使用机器学习的方式给该商机打的分,该分数代表选中商机的赢单概率。
上面显示的分数是基于SAP C4C tenant上过去12个月的销售数据,经过训练之后的机器学习模型计算出来的。分数越高,赢单率越大,因此销售代表可以更有针对性的把资源放在优先级更高的商机上去。分数会每天更新一次。
为了让机器学习计算出来的得分更准确,需要C4C系统里至少存在5000条历史商机数据,并且这些历史商机数据里的"赢单"或者"输单"状态尽可能均匀分布。
Insights标签页里显示的分数和Key Feature(关键指标)全部是从C4C后台通过HTTP请求,以JSON格式返回到前台进行渲染。
这个JSON格式的响应明细如下(从Chrome开发者工具Network标签页里观察到的):
机器学习在C4C销售报价单的产品推荐场景中的作用
大家平时在京东或淘宝上买一个东西后,手机app会自动向我们推荐一些其他我们可能会购买的商品,这些推荐就是背后的机器学习框架基于我们以前的购买习惯通过一定的算法计算出来的。
C4C同样支持使用机器学习根据销售订单历史数据进行向上销售(Up Selling)和交叉销售(Cross Selling)产品推荐。
我们可以在Machine Learning Scenarios(机器学习场景)的列表里看到Product Recommendation(产品推荐)这个场景。通过点击按钮Add Model创建一个新的机器学习模型,点击Train进行训练,确保训练成功完成,状态变为Active, 说明该模型可用。
创建一个新的Product List,里面包含了需要销售的产品:下面的例子有两个产品,ID为为1042416和10001380。
如果是传统的产品推荐场景,假设当我在销售订单的行项目里维护了上述两个产品的ID后,还想推荐一些其他的产品,则需要通过人工的方式将这些推荐的商品维护到Product list的"Proposed Products"标签页里,如下图红色区域所示。
有了人工智能加上机器学习后,就可以省去这些人工配置的步骤和工作量。我给这个Product List加上了一个"203 - Product Recommendation"的场景,如下图蓝色区域所示,希望让这个Product List里包含的产品被加入到销售订单时,通过人工智能的方式由SAP C4C系统自动推荐相关产品。
现在我们来做个测试,创建一个新的销售报价单,将之前维护在Product List的某一个产品,比如1042416,维护在这个销售报价单的行项目里,然后C4C系统自动给我推荐了两个其他产品,ID为P140101和P140100。
下图是我从ABAP后台调试机器学习API调用得到的JSON响应在JSON编辑器里打开的截图。可以看到机器学习给ID为P140101和P140100这两个产品计算的相关分数是90和83。因为机器学习API的具体细节在SAP帮助文档里没有提及,这里不便介绍。
关于机器学习在C4C中的更多应用,请参考SAP帮助文档。感谢阅读。
要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:
人工智能
2018-10-22 14:00:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
很多SAP顾问朋友们对于人工智能/机器学习这个话题非常感兴趣,也在不断思考如何将这种新技术和SAP传统产品相结合。Jerry之前的公众号文章 C4C和微信集成系列教程 曾经介绍了Partner如何利用SAP Cloud for Customer内置的社交媒体集成框架,做一些简单的开发将微信集成到C4C中去。该系列教程里其中一篇文章,介绍了如何将图灵机器人集成到某个公众号的消息服务器上去,让该公众号可以“智能地”同其关注粉丝聊天。
这个图灵机器人的图灵服务通过Restful API的方式暴露出来,所有和机器学习相关的细节都被www.tuling123.com封装成黑盒子,应用开发人员只管消费。这是第三方的人工智能解决方案。
2018年1月,SAP收购了一家法国的人工智能提供商,Recast.AI, 从此SAP也有了自己的人工智能解决方案。
现在,访问 https://recast.ai , 能看到一行醒目的提示: Recast.AI is now part of SAP 。那么Recast.AI提供了怎样的功能呢?最好的入门办法,莫过于自己动手做一个Hello World例子出来。跟着Jerry一块做一个吧。
我们的目的同本文开头截图展示的一致:开发一个在微信里使用的聊天机器人,不同之处在于这次我们使用 SAP Recast.AI 作为人工智能解决方案。
绝大多数SAP顾问朋友们关注的是如何利用人工智能基于SAP传统产品做创新,给客户带来价值,而不是花大量时间精力去学习机器学习底层需要的那些艰深的数学知识。Recast.AI也完美地将不需要应用开发人员了解的机器学习底层细节进行了封装,我们将要做的这个聊天机器人甚至不需要太多的编码。
我们在Recast.AI网站上在线创建聊天机器人。一般网站首次登陆都需要先创建用户,而Recast.AI除了常规用户注册方式外,还贴心地提供了使用Github账号直接登录的选择,对于每天混Github的程序员来说,这太方便了。
点击按钮允许Recast.AI访问您Github账号的公有信息后,能看到这个欢迎界面:
点"START WITH A TEMPLATE"基于现成的模板创建一个机器人:
和我们在Fiori UI里进行业务模型创建类似,首先维护机器人的ID和描述信息:
创建成功后,来到如下的机器人明细页面,一共包含5个标签。本文只会用到Train, Build和Connect这三个标签。
Train标签下没有任何内容,只有一个提示:You have no intents.
什么是intent呢?在Android开发里也有intent的概念,intent是一个将要执行的动作的抽象描述。Android框架根据此intent的描述,负责找到将要执行动作对应的组件,再把包含了附加参数的intent传递给决定出的负责组件。而在Recast.AI里的intent同样是一个抽象概念。通俗地解释,intent是一系列句子的集合,这些句子虽然从文字上来说表达方式各异,然而传递的是同一个意思。
比如下列三句话其实是在同一个问题: 你是一个程序员么? 你每天的工作是编写代码么? 你每天使用集成开发环境或者命令行进行单步调试么?
我们可以把这三个句子归为同一个intent,姑且命名为"程序员"。假设当用户又发送了一个新的文本到您的聊天机器人去:“你是靠编写代码维生么?” Recast.AI封装好的算法会解析这个文本,将其和"程序员"intent里的语句进行比较,如果判断出来这个新的文本和intent中的某一句表达的意思非常接近,那么我们就能说这个新文本的intention(意图)是"程序员"。此时,就可以让机器人回复预先准备好的回答,比如“是的,我是程序员,我很喜欢写代码”。
上面的描述实际上已经涵盖了本文聊天机器人创建完毕后,需要执行哪些后续步骤。
回到我创建的聊天机器人,目前没有分配任何intent。
那么我就点Create按钮创建一个。当然也可以点Search按钮,从Recast社区上搜索一个别人创建好的intent。
现在新建的这个programmer intent还没有任何语句,现在我们就来给它“喂”一些句子,这个动作称为"Train(训练)"。输入一个句子"Are you a programmer?",回车:
Recast.AI会自动将这个句子进行分词,并且识别出一个代词(Pronoun) - You, 一个数量词(Number) - a, 和一个工作描述(Job) - programmer。
我们再多喂几个句子给这个intent: Are you coding with Java or ABAP or JavaScript or C# or C++ or Python or Ruby? Are you using Integrated Development Environment or Command Line to debug? is Programming your daily work?
什么时候可以结束训练呢?首先给intent喂的句子越多,那么它分析新的语句的意图(intention)是否和程序员相关的准确度就越高。现在我们可以对这个intent进行测试。点击Train标签页右上角的Test链接,会看到下图的测试窗口。现在我给这个intent发送一条文本:
Are you working with Java every day?
Recast.AI将这个句子同我创建的"programmer" intent里的句子进行比较,最后判断出,这句话有99%的可能性属于该intent(下图字段confidence: 0.99)。
那么我们就结束训练,进入下一个"Build"步骤。这个步骤负责定义当您的聊天机器人成功识别到了您的输入是在讨论程序员后,应该执行何种动作。Recast.AI把机器人这种成功识别出文本的intention后执行某种动作的能力,称为skill。在Build标签页点击Create skill:
我给新建的skill取名:chat_with_programmer
给这个skill定义Triggers,意思是满足何种条件会触发这个skill?在Triggers标签页下面使用Recast.AI预置的一些公式进行条件定义。
下图if @programmer ,意思就是:如果聊天机器人计算出用户发送的文本是属于"programmer"这个intent:
Actions就是在这种条件下应该执行的具体动作。
这里我定义的Actions就是简单地推送一些文本(SEND MESSAGE)给用户。
准备一些文本,机器人会从中间随机地选取一条推送给用户。 Yes, and I am learning Scala in my spare time. Yes, I have been programming for 20 years. Yes, I am a programmer, I love programming!
点击Build标签页的"CHAT WITH YOUR BOT"进行测试。随便输入一句和程序员相关的话,比如"Are you working with Java?" 在测试页面,看到了机器人自动给我推送了一个回复,同时显示了我之前创建的"chat_with_programmer" skill已经被触发(triggered)了。这意味着Build标签页里的配置也已经完成。
最后,我们可以把这个创建好的聊天机器人进行发布,让它可以被其他平台消费。这里我选择的是Webchat渠道,意思是在一个网页内嵌入这个聊天机器人的对话窗口。
点击Connect标签页,选择Webchat:
可以对嵌入的聊天机器人的会话窗口的某些属性进行定制化,比如窗口标题,用户和机器人的图片定制等。
点击SAVE CHANGES,会生成一段包含Script标签的HTML代码。直接拷贝到某个网页里,就能使用了。
比如您可以把这个网页作为HTML5应用部署到SAP云平台上,得到一个URL,然后把这个URL绑定到公众号的某个自定义菜单上。当用户点击了这个微信菜单后,就会在微信里打开聊天机器人的会话窗口。
具体步骤在我的博客里有介绍:
Wechat development series 5 – embedded your UI5 application to Wechat app
https://blogs.sap.com/2017/12/17/wechat-development-series-5-embedded-your-ui5-application-to-wechat-app/
下图是我的聊天机器人在微信里工作的效果图。
本文这个例子的完整步骤在SAP Recast.AI的官网上也有记载,不过是全英文的。大家有兴趣可以动手做一做。感谢阅读。
https://recast.ai/blog/build-your-first-bot-with-recast-ai/
要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:
人工智能
2018-10-22 13:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
大家平时在京东或淘宝上买一个东西后,手机app会自动向我们推荐一些其他我们可能会购买的商品,这些推荐就是背后的机器学习框架基于我们以前的购买习惯通过一定的算法计算出来的。
SAP的一款CRM云解决方案,Cloud for Customer(简称C4C),同样支持使用机器学习根据销售订单历史数据进行向上销售和交叉销售机会的产品推荐。
下面我们一起来看看人工智能在产品推荐这个场景里的具体实现吧。还是先去Administrator->Prediction Services,点击Model Setup进行机器学习的模型设置。
我们可以在Machone Learning Scenarios(机器学习场景)的列表里看到Product Recommendation(产品推荐)这个场景。通过点击按钮“Add Model”创建一个新的机器学习模型,点击“Train”进行训练,确保训练成功完成,状态变为"Active", 说明该模型可用。
创建一个新的Product List,里面包含了需要销售的产品:下面的例子有两个产品,ID为为1042416和10001380。
如果是传统的产品推荐场景,假设当我在销售订单的行项目里维护了上述两个产品的ID后,还想推荐一些其他的产品,则需要通过人工的方式将这些推荐的商品维护到Product list的"Proposed Products"标签页里,如下图红色区域所示。
有了人工智能加上机器学习后,就可以省去这些人工配置的步骤和工作量。我给这个Product List加上了一个"203 - Product Recommendation"的场景,如下图蓝色区域所示,希望让这个Product List里包含的产品被加入到销售订单时,通过人工智能的方式由SAP C4C系统自动推荐相关产品给我。
现在我们来做个测试,创建一个新的销售报价单,将之前维护在Product List的某一个产品,比如1042416,维护在这个销售报价单的行项目里,然后C4C系统自动给我推荐了两个其他产品,ID为P140101和P140100。

要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:
人工智能
2018-10-21 11:48:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
用于支持论文《Relational inductive biases, deep learning, and graph networks》。
github
A graph network takes a graph as input and returns a graph as output. The input graph has edge- ( E ), node- ( V ), and global-level ( u ) attributes. The output graph has the same structure, but updated attributes. Graph networks are part of the broader family of "graph neural networks" (Scarselli et al., 2009).
讲直白一些,就是用神经网络处理图,输入是图,输出也是图。以前都是处理向量(Vector),所以NLP中需要做Word2Vec后才能运用深度学习的处理结果。作者们认为 Graph2Graph 是让神经网络具备推理(Reason)能力的一个关键步骤。
【CNN已老,GNN来了】DeepMind、谷歌大脑、MIT等27位作者重磅论文,图网络让深度学习也能因果推理
人工智能
2018-10-20 18:32:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
OpenVINO目的是为了在应用环境部署深度学习的模型用于识别,更专业的说法是Predict。四天前Intel在OpenCV项目下开放源代码。并有多种训练模型。可以加速计算机视觉的的产品开发。
Github
Model zoo



人工智能
2018-10-20 18:22:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
使用机器学习进行客户管理,我们可以得到客户360度全方位的视图。
本文以SAP Cloud for Customer的客户管理应用为例,介绍机器学习是如何同传统的客户管理应用进行集成的。
打开SAP C4C的客户中心,在客户列表里选中任意一个客户,能在右边看到一个名为Insights的页面。
这些客户的360度视图是基于C4C内部和外部的数据源分析得出的,有助于销售人员进行更有针对性的客户计划和销售。C4C的外部数据源采用的是第三方数据提供商Bombora。
通过Insights面板,我们能够获得通过机器学习得出的每个客户的购买倾向的分数,也能看出就我们关注的某一话题,该客户的行为和倾向到底如何。Bombora会从该客户相关的B2B网站上捕捉能够反映该客户购买倾向的各种线索。当检测到客户在某个话题上的线索数量有明显增加时,我们称这个客户就该话题表现出了一个Surge。我们会给出Surge的分数,范围在1到99之间,每周更新一次。
SAP C4C会将某个客户总的Surge分数显示在屏幕右侧Insights面板内,同时显示出Surge分数最高的前三个话题。下图Surge分数前三的话题依次为:Artificial Intelligence, Machine Learning和Collaboration Software。
在C4C工作中心视图Predication Services的Third Party Data可以对Insights面板里需要关注的话题进行配置:

Activity Management(活动管理)
显示和该客户过去一年内接收和发送的电子邮件,电话,会议,以及后续任务(Followup-task)的总数。
Pipeline(销售管道)
销售管道有时候也叫销售漏斗,是一个非常形象的概念,是销售过程控制的重要分析工具,适合销售流程比较规范,周期比较长,参与的人员比较多的复杂销售过程的管理。顾名思义,所有具有购买需求的潜在用户位于销售漏斗的顶部。 漏斗的上部:将自己企业的产品列入候选清单的潜在用户。 漏斗的中部:将本企业产品列入优选清单的潜在用户(如两个互为竞品的品牌中选一个) 漏斗的下部:基本上已经确定购买本企业的产品,只是有些手续还没有落实的潜在用户。 漏斗的底部:期望能够成交的用户。
根据所处整个销售过程的位置,以及销售成功的概率,我们通常需要对处于漏斗各个层次的销售状态确定一个百分数来代表该用户销售的成功率。
比如,处在漏斗上部的潜在用户其成功率为20%,处在漏斗中部的潜在用户其成功率为50%,处在漏斗下部的潜在用户其成功率为 70%。从潜在用户到最后成功签约,流程每推进一步,用户名单就会减少一些,看起来是个倒金字塔形状,也就是个漏斗形状,所以将其形象地称为销售漏斗。
SAP C4C的Insights标签会显示和该客户相关的销售漏斗涉及的总金额,以及当前处于正在进行中的销售机会数和销售报价单个数。
Win Rate(赢单率)
基于过去一年销售数据计算出的赢单率,赢下的单子总的金额以及输掉的单子对应的总金额。
Total Contract Value(合同总金额)
和该客户相关的销售和服务合同的总金额。
Account Receivables:针对某客户的应收款金额。
要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:
人工智能
2018-10-20 10:22:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
卷积神经网络CNN代码解析 deepLearnToolbox-master是一个深度学习matlab包,里面含有很多机器学习算法,如卷积神经网络CNN,深度信念网络DBN,自动编码AutoE
ncoder(堆栈SAE,卷积CAE)的作者是 RasmusBerg Palm

今天给介绍deepLearnToolbox-master中的CNN部分。 DeepLearnToolbox-master中CNN内的函数:
该模型使用了mnist的数字mnist_uint8.mat作为训练样本,作为cnn的一个使用样例,每个样本特征为一个28*28=的向量。
网络结构为:
-让我们来分析各个函数: 一、Test example CNN
三、cnntrain.m.
四、cnnff.m.
五、cnnbp.m.
五、cnnapplygrads.m.
六、cnntest.m.

一、Test example CNN:
1设置CNN的基本参数规格,如卷积、降采样层的数量,卷积核的大小、降采样的降幅
2 cnnsetup函数 初始化卷积核、偏置等
3 cnntrain函数 训练cnn,把训练数据分成batch,然后调用
3.1 cnnff 完成训练的前向过程,
3.2 cnnbp计算并传递神经网络的error,并计算梯度(权重的修改量)
3.3 cnnapplygrads 把计算出来的梯度加到原始模型上去
4 cnntest函数,测试当前模型的准确率
该模型采用的数据为mnist_uint8.mat,
含有70000个手写数字样本其中60000作为训练样本,10000作为测试样本。
把数据转成相应的格式,并归一化。

设置网络结构及训练参数

初始化网络,对数据进行批训练,验证模型准确率

绘制均方误差曲线

二、Cnnsetup.m
该函数你用于初始化CNN的参数。
设置各层的mapsize大小,
初始化卷积层的卷积核、bias
尾部单层感知机的参数设置
bias统一设置为0
权重设置为:-1~1之间的随机数/sqrt(6/(输入神经元数量+输出神经元数量))
对于卷积核权重,输入输出为fan in, fan out
fan_out= net.layers{l}.outputmaps * net.layers{l}.kernelsize ^ 2;
%卷积核初始化,1层卷积为1 6个卷积核,2层卷积一共6 12=72个卷积核。对于每个卷积输出featuremap,
%fan_in= 表示该层的一个输出map,所对应的所有卷积核,包含的神经元的总数。1 25,6 25
fan_in =numInputmaps * net.layers{l}.kernelsize ^ 2;
fin=1 25 or 6 25
fout=1 6 25 or 6 12 25
net.layers{l}.k{i}{j} =(rand(net.layers{l}.kernelsize) - 0.5) * 2 * sqrt(6 / (fan in + fan out));

1、卷积降采样的参数初始化


2、尾部单层感知机的参数(权重和偏量)设置:

三、cnntrain.m
该函数用于训练CNN。
生成随机序列,每次选取一个batch(50)个样本进行训练。
批训练:计算50个随机样本的梯度,求和之后一次性更新到模型权重中。
在批训练过程中调用:
Cnnff.m 完成前向过程
Cnnbp.m 完成误差传导和梯度计算过程
Cnnapplygrads.m把计算出来的梯度加到原始模型上去


四、cnnff.m
1、取得CNN的输入

2、两次卷积核降采样层处理

3、尾部单层感知机的数据处理,需要把subFeatureMap2连接成为一个(4 4) 12=192的向量,但是由于采用了50样本批训练的方法,subFeatureMap2被拼合成为一个192*50的特征向量fv;
Fv作为单层感知机的输入,全连接的方式得到输出层

五、cnnbp.m
该函数实现2部分功能,计算并传递误差,计算梯度
1、计算误差和LossFunction

2、计算尾部单层感知机的误差

3、把单层感知机的输入层featureVector的误差矩阵,恢复为subFeatureMap2的4*4二维矩阵形式

插播一张图片:

4、误差在特征提取网络【卷积降采样层】的传播
如果本层是卷积层,它的误差是从后一层(降采样层)传过来,误差传播实际上是用降采样的反向过程,也就是降采样层的误差复制为2*2=4份。卷积层的输入是经过sigmoid处理的,所以,从降采样层扩充来的误差要经过sigmoid求导处理。
如果本层是降采样层,他的误差是从后一层(卷积层)传过来,误差传播实际是用卷积的反向过程,也就是卷积层的误差,反卷积(卷积核转180度)卷积层的误差,原理参看插图。

5、计算特征抽取层和尾部单层感知机的梯度

五、cnnapplygrads.m
该函数完成权重修改,更新模型的功能 更新特征抽取层的权重 weight+bias 更新末尾单层感知机的权重 weight+bias

六、cnntest.m
验证测试样本的准确率
阅读原文
人工智能
2018-10-19 16:09:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
10月17日是国家扶贫日,又适逢重阳节,阿里巴巴公益向全社会发起“我的父老乡亲”大型公益征文活动,用你的笔,让埋藏在心底的励志故事再次发光。
讲一个故事,赢万元奖励。你准备好了吗?
改革开放四十年,弹指一挥间。
曾经,工厂林立与车水马龙代表着城市的繁华;
如今,绿水青山与鸟语花香成为了所有人梦想的桃源;
曾经,手表、自行车与缝纫机是家庭小康的最高象征;
如今,身体健康与精神富足成为了千家万户的渴盼;
曾经,我们手捧着厚厚的相册,听老人讲着那过去的故事;
如今,我们打开手机,与全家分享远古的浩渺与星空的璀璨。
世界在改变,我们也是。
当我们蓦然回首,无论是60后还是00后,每一代人都有对生活的独特感知与专属记忆;
当我们颔首向前,无论是耄耋老人还是懵懂稚子,每个人都有对于未来的美好期待。
这是最好的时代,也是最值得被记录的时代。
斗转星移,日新月异,每个人都是时间的观察者,也是历史的记录者。
人工智能
2018-10-19 13:45:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Hanlp中使用纯JAVA实现CRF分词
与基于隐马尔可夫模型的最短路径分词、N-最短路径分词相比,基于条件随机场(CRF)的分词对未登录词有更好的支持。本文(HanLP)使用纯Java实现CRF模型的读取与维特比后向解码,内部特征函数采用 双数组Trie树(DoubleArrayTrie)储存,得到了一个高性能的中文分词器。
开源项目
本文代码已集成到HanLP中开源: http://hanlp.com/
CRF简介
CRF是序列标注场景中常用的模型,比HMM能利用更多的特征,比MEMM更能抵抗标记偏置的问题。
CRF训练
这类耗时的任务,还是交给了用C++实现的CRF++。关于CRF++输出的CRF模型,请参考《CRF++模型格式说明》。
CRF解码
解码采用维特比算法实现。并且稍有改进,用中文伪码与白话描述如下:
首先任何字的标签不仅取决于它自己的参数,还取决于前一个字的标签。但是第一个字前面并没有字,何来标签?所以第一个字的处理稍有不同,假设第0个字的标签为X,遍历X计算第一个字的标签,取分数最大的那一个。
如何计算一个字的某个标签的分数呢?某个字根据CRF模型提供的模板生成了一系列特征函数,这些函数的输出值乘以该函数的权值最后求和得出了一个分数。该分数只是“点函数”的得分,还需加上“边函数”的得分。边函数在本分词模型中简化为f(s',s),其中s'为前一个字的标签,s为当前字的标签。于是该边函数就可以用一个4*4的矩阵描述,相当于HMM中的转移概率。
实现了评分函数后,从第二字开始即可运用维特比后向解码,为所有字打上BEMS标签。
实例
还是取经典的“商品和服务”为例,首先HanLP的CRFSegment分词器将其拆分为一张表:
null表示分词器还没有对该字标注。
代码
上面说了这么多,其实我的实现非常简练:

标注结果
标注后将table打印出来:
最终处理
将BEMS该合并的合并,得到:
然后将词语送到词典中查询一下,没查到的暂时当作nx,并记下位置(因为这是个新词,为了表示它的特殊性,最后词性设为null),再次使用维特比标注词性:
新词识别
CRF对新词有很好的识别能力,比如:
输出:
null表示新词。
转载自hankcs的博客
人工智能
2018-10-19 10:18:07
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.缺失值处理 - 拉格朗日插值法
input_file数据文件内容(存在部分缺失值):
from scipy.interpolate import lagrange import pandas as pd import numpy as np input_file = './data/catering_sale.xls' output_file = './data/sales.xls' data = pd.read_excel(input_file) data['销量'][(data['销量'] < 400) | (data['销量'] > 5000)] = None  # 销量小于400及大于5000的视为异常值,置为None # 自定义列向量插值函数 # 问题:当n
output_file结果:

# np.where() a = pd.Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan], index=['f', 'e', 'd', 'c', 'b', 'a']) b = pd.Series(np.arange(len(a), dtype=np.float64), index=['f', 'e', 'd', 'c', 'b', 'a']) # 如果a有缺失值,则用相应位置的b填充,否则使用a的原有元素 print(np.where(pd.isnull(a), b, a)) # result [ 0. 2.5 2. 3.5 4.5 5. ]
# df.combine_first() df1 = pd.DataFrame({'a': [1., np.nan, 5., np.nan], 'b': [np.nan, 2., np.nan, 6.], 'c': range(2, 18, 4)}) df2 = pd.DataFrame({'a': [5., 4., np.nan, 3., 7.], 'b': [np.nan, 3., 4., 6., 8.]}) # 将df1中的缺失值用df2中相同位置的元素填充,如果没有缺失值则保持df1的原有元素 df1.combine_first(df2) # result a b c 0 1.0 NaN 2.0 1 4.0 2.0 6.0 2 5.0 4.0 10.0 3 3.0 6.0 14.0 4 7.0 8.0 NaN
# 异常值处理 data = pd.DataFrame(np.random.randn(1000, 4)) print(data.describe()) # result 0 1 2 3 count 1000.000000 1000.000000 1000.000000 1000.000000 mean -0.012809 0.007609 -0.002442 0.027889 std 1.026971 0.985884 0.999810 1.006344 min -3.174895 -2.970125 -3.011063 -3.440525 25% -0.723649 -0.657574 -0.642299 -0.647432 50% -0.019972 0.021018 -0.015020 0.012603 75% 0.707184 0.678987 0.674781 0.707672 max 3.076159 3.890196 2.869127 3.089114 col = data[3] # 大于3的值为异常值 col[np.abs(col) > 3] data[(np.abs(data) > 3).any(1)] # any(1) # np.sign()函数,大于0为1,小于0为-1 data[np.abs(data) > 3] = np.sign(data) * 3 print(data.describe()) # result 0 1 2 3 count 1000.000000 1000.000000 1000.000000 1000.000000 mean -0.012763 0.006719 -0.002428 0.028545 std 1.026062 0.982772 0.999768 1.003687 min -3.000000 -2.970125 -3.000000 -3.000000 25% -0.723649 -0.657574 -0.642299 -0.647432 50% -0.019972 0.021018 -0.015020 0.012603 75% 0.707184 0.678987 0.674781 0.707672 max 3.000000 3.000000 2.869127 3.000000

2.数据合并:
# pd.merge() # 使用列或者索引,以类似数据库连接的方式合并多个DataFrame对象 df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)}) df2 = pd.DataFrame({'key': ['a', 'b', 'd'], 'data2': range(3)}) print(pd.merge(df1, df2)) # 自动匹配合并列, 默认内连接 print(pd.merge(df1, df2, on='key')) # 显式指定 # result
data1 key data2
0 0 b 1
1 1 b 1
2 6 b 1
3 2 a 0
4 4 a 0
5 5 a 0 df3 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)}) df4 = pd.DataFrame({'rkey': ['a', 'b', 'd'], 'data2': range(3)}) print(pd.merge(df3, df4, left_on='lkey', right_on='rkey')) # 当不存在相同column时,需要分别指定连接列名 # result
data1 lkey data2 rkey
0 0 b 1 b
1 1 b 1 b
2 6 b 1 b
3 2 a 0 a
4 4 a 0 a
5 5 a 0 a
## 指定连接方式 # 外连接 print(pd.merge(df1, df2, how='outer')) # result data1 key data2 0 0.0 b 1.0 1 1.0 b 1.0 2 6.0 b 1.0 3 2.0 a 0.0 4 4.0 a 0.0 5 5.0 a 0.0 6 3.0 c NaN 7 NaN d 2.0
# 左连接 df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'], 'data1': range(6)}) df2 = pd.DataFrame({'key': ['a', 'b', 'a', 'b', 'd'] ,'data2': range(5)}) print(pd.merge(df1, df2, how='left')) # result data1 key data2 0 0 b 1.0 1 0 b 3.0 2 1 b 1.0 3 1 b 3.0 4 2 a 0.0 5 2 a 2.0 6 3 c NaN 7 4 a 0.0 8 4 a 2.0 9 5 b 1.0 10 5 b 3.0
# 多列连接 left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'], 'key2': ['one', 'two', 'one'], 'lval': [1, 2, 3]}) right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'], 'key2': ['one', 'one', 'one', 'two'], 'rval': [4, 5, 6, 7]}) print(pd.merge(left, right, on=['key1', 'key2'])) # 默认内连接 # result key1 key2 lval rval 0 foo one 1 4 1 foo one 1 5 2 bar one 3 6 print(pd.merge(left, right, on=['key1', 'key2'], how='outer')) # 外连接 # result key1 key2 lval rval 0 foo one 1.0 4.0 1 foo one 1.0 5.0 2 foo two 2.0 NaN 3 bar one 3.0 6.0 4 bar two NaN 7.0
# 只以其中一个列连接,会出现冗余列 pd.merge(left, right, on='key1') # result key1 key2_x lval key2_y rval 0 foo one 1 one 4 1 foo one 1 one 5 2 foo two 2 one 4 3 foo two 2 one 5 4 bar one 3 one 6 5 bar one 3 two 7 print(pd.merge(left, right, on='key1', suffixes=('_left', '_right'))) # 给冗余列增加后缀 # result key1 key2_left lval key2_right rval 0 foo one 1 one 4 1 foo one 1 one 5 2 foo two 2 one 4 3 foo two 2 one 5 4 bar one 3 one 6 5 bar one 3 two 7
# 使用索引与列进行合并 left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],'value': range(6)}) right1 = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b']) print(pd.merge(left1, right1, left_on='key', right_index=True)) # left1使用key列连接,right1使用index列连接 # result key value group_val 0 a 0 3.5 2 a 2 3.5 3 a 3 3.5 1 b 1 7.0 4 b 4 7.0
# 多列索引连接 lefth = pd.DataFrame({'key1': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'], 'key2': [2000, 2001, 2002, 2001, 2002], 'data': np.arange(5.)}) righth = pd.DataFrame(np.arange(12).reshape((6, 2)), index=[['Nevada', 'Nevada', 'Ohio', 'Ohio', 'Ohio', 'Ohio'], [2001, 2000, 2000, 2000, 2001, 2002]], columns=['event1', 'event2']) print(pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True)) # result
data key1 key2 event1 event2
0 0.0 Ohio 2000 4 5
0 0.0 Ohio 2000 6 7
1 1.0 Ohio 2001 8 9
2 2.0 Ohio 2002 10 11
3 3.0 Nevada 2001 0 1
# pd.join() # pd.join()可以使用index或key合并两个及以上的DataFrame(列方向上的合并) left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]], index=['a', 'c', 'e'], columns=['Ohio', 'Nevada']) right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]], index=['b', 'c', 'd', 'e'], columns=['Missouri', 'Alabama']) print(left2.join(right2, how='outer')) # result Ohio Nevada Missouri Alabama a 1.0 2.0 NaN NaN b NaN NaN 7.0 8.0 c 3.0 4.0 9.0 10.0 d NaN NaN 11.0 12.0 e 5.0 6.0 13.0 14.0
# 合并多个DataFrame another = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]], index=['a', 'c', 'e', 'f'], columns=['New York', 'Oregon']) left2.join([right2, another], how='outer') # result Ohio Nevada Missouri Alabama New York Oregon a 1.0 2.0 NaN NaN 7.0 8.0 b NaN NaN 7.0 8.0 NaN NaN c 3.0 4.0 9.0 10.0 9.0 10.0 d NaN NaN 11.0 12.0 NaN NaN e 5.0 6.0 13.0 14.0 11.0 12.0 f NaN NaN NaN NaN 16.0 17.0
# 轴向连接# np.concatenate() arr = np.arange(12).reshape((3,4)) print(np.concatenate([arr, arr], axis=1)) # 在column方向上连接 # result array([[ 0, 1, 2, ..., 1, 2, 3], [ 4, 5, 6, ..., 5, 6, 7], [ 8, 9, 10, ..., 9, 10, 11]])
# pd.concat() s1 = pd.Series([0,1], index=['a', 'b']) s2 = pd.Series([2, 3, 4], index=['c', 'd', 'e']) s3 = pd.Series([5, 6], index=['f', 'g']) print(pd.concat([s1, s2, s3])) # axis参数默认为0,row方向的 # result a 0 b 1 c 2 d 3 e 4 f 5 g 6 dtype: int64 print(pd.concat([s1, s2, s3], axis=1)) # column方向合并,值如果不存在则记为NaN # result 0 1 2 a 0.0 NaN NaN b 1.0 NaN NaN c NaN 2.0 NaN d NaN 3.0 NaN e NaN 4.0 NaN f NaN NaN 5.0 g NaN NaN 6.0 s4 = pd.concat([s1 * 5, s3]) s5 = pd.concat([s1, s4], axis=1) s5.columns = ['s1', 's4'] print(s5) # result s1 s4 a 0.0 0 b 1.0 5 f NaN 5 g NaN 6 print(pd.concat([s1, s4], axis=1, join='inner')) # join参数指定连接方式 # result 0 1 a 0 0 b 1 5 print(pd.concat([s1, s4], axis=1, join_axes=[['a', 'c', 'b', 'e']])) # 手动指定要连接的index # result 0 1 a 0.0 0.0 c NaN NaN b 1.0 5.0 e NaN NaN
# 使用keys参数对索引进行分级 result = pd.concat([s1, s2, s3], keys=['one', 'two', 'three']) # 在row方向合并时,keys对应每个Series的一级index,每个Series原有的index则作为二级index print(result) # result one a 0 b 1 two c 2 d 3 e 4 three f 5 g 6 dtype: int64
# Series.unstack() 将Seris格式转换为DataFrame格式 print(result.unstack()) # 一级索引将作为index,二级索引作为columns # result a b c d e f g one 0.0 1.0 NaN NaN NaN NaN NaN two NaN NaN 2.0 3.0 4.0 NaN NaN three NaN NaN NaN NaN NaN 5.0 6.0
# 在列合并时使用keys参数指定column名称 print(pd.concat([s1, s2, s3], axis=1, keys=['one', 'two', 'three'])) # 在column方向合并时,keys对应每个合并的Series的column # result one two three a 0.0 NaN NaN b 1.0 NaN NaN c NaN 2.0 NaN d NaN 3.0 NaN e NaN 4.0 NaN f NaN NaN 5.0 g NaN NaN 6.0
# 指定分级column df1 = pd.DataFrame(np.arange(6).reshape(3, 2), index=['a', 'b', 'c'], columns=['one', 'two']) df2 = pd.DataFrame(5 + np.arange(4).reshape(2, 2), index=['a', 'c'], columns=['three', 'four']) # 因为DataFrame对象已经有了column,所以keys参数会设置新的一级column, df原有的column则作为二级column df3 = pd.concat([df1, df2], axis=1, keys=['level1', 'level2']) print(df3) print(df3.columns) # result level1 level2 one two three four a 0 1 5.0 6.0 b 2 3 NaN NaN c 4 5 7.0 8.0 MultiIndex(levels=[['level1', 'level2'], ['four', 'one', 'three', 'two']], labels=[[0, 0, 1, 1], [1, 3, 2, 0]]) # 使用字典实现相同的功能 print(pd.concat({'level1': df1, 'level2': df2}, axis=1)) #result level1 level2 one two three four a 0 1 5.0 6.0 b 2 3 NaN NaN c 4 5 7.0 8.0 # 指定分级column名称 df = pd.concat([df1, df2], axis=1, keys=['level1', 'level2'], names=['levels', 'number']) print(df) print(df.columns) # result levels level1 level2 number one two three four a 0 1 5.0 6.0 b 2 3 NaN NaN c 4 5 7.0 8.0 MultiIndex(levels=[['level1', 'level2'], ['four', 'one', 'three', 'two']], labels=[[0, 0, 1, 1], [1, 3, 2, 0]], names=['levels', 'number'])
# ignore_index df1 = pd.DataFrame(np.random.randn(3, 4), columns=['a', 'b', 'c', 'd']) df2 = pd.DataFrame(np.random.randn(2, 3), columns=['b', 'd', 'a']) # row方向忽略索引 print(pd.concat([df1, df2], ignore_index=True)) # result a b c d 0 1.261208 0.022188 -2.489475 -1.098245 1 0.618618 -1.179827 1.475738 0.334444 2 -0.319088 -0.153492 0.029245 0.336055 3 -0.999023 -0.502154 NaN 0.722256 4 1.428007 -0.726810 NaN 0.432440 # column方向忽略列名 print(pd.concat([df1, df2], axis=1, ignore_index=True)) # result 0 1 2 3 4 5 6 0 1.261208 0.022188 -2.489475 -1.098245 -0.502154 0.722256 -0.999023 1 0.618618 -1.179827 1.475738 0.334444 -0.726810 0.432440 1.428007 2 -0.319088 -0.153492 0.029245 0.336055 NaN NaN NaN
3.重塑层次化索引 data = pd.DataFrame(np.arange(6).reshape((2, 3)), index=pd.Index(['Ohio', 'Colorado'], name='state'), columns=pd.Index(['one', 'two', 'three'], name='number')) # 轴向旋转 result = data.stack() print(result) # result state number Ohio one 0 two 1 three 2 Colorado one 3 two 4 three 5 # 还原操作 print(result.unstack()) # result number one two three state Ohio 0 1 2 Colorado 3 4 5 # 行列转置 print(result.unstack(0)) # result state Ohio Colorado number one 0 3 two 1 4 three 2 5 # 指定要转置的索引名 print(result.unstack('number')) # result number one two three state Ohio 0 1 2 Colorado 3 4 5
# 例1: s1 = pd.Series([0, 1, 2, 3], index=['a', 'b', 'c', 'd']) s2 = pd.Series([4, 5, 6], index=['c', 'd', 'e']) data2 = pd.concat([s1, s2], keys=['one', 'two']) print(data2.unstack()) # result a b c d e one 0.0 1.0 2.0 3.0 NaN two NaN NaN 4.0 5.0 6.0 print(data2.unstack().stack()) # result one a 0.0 b 1.0 c 2.0 d 3.0 two c 4.0 d 5.0 e 6.0 dtype: float64 # 不dropnan值 print(data2.unstack().stack(dropna=False)) # result one a 0.0 b 1.0 c 2.0 d 3.0 e NaN two a NaN b NaN c 4.0 d 5.0 e 6.0 dtype: float64
# 例2: df = pd.DataFrame({'left': result, 'right': result + 5}, columns=pd.Index(['left', 'right'], name='side')) print(df.unstack('state')) # result side left right state Ohio Colorado Ohio Colorado number one 0 3 5 8 two 1 4 6 9 three 2 5 7 10 print(df.unstack('state').stack('side')) # result state Colorado Ohio number side one left 3 0 right 8 5 two left 4 1 right 9 6 three left 5 2 right 10 7

4.长宽格式的转换:
所谓长格式,即相关属性都集中在同一个列中,另有一个VALUE列对应相应的属性值;
而宽格式, 就是各个属性自成一列,不需要单独的VALUE列。 # 导入宽格式数据 data = pd.read_csv('./data/macrodata.csv') # pd.PeriodIndex 用来存放表示周期性日期的数组,数组元素是不可更改的。例如:年、季度、月、天等。 periods = pd.PeriodIndex(year=data.year, quarter=data.quarter, name='date') data = pd.DataFrame(data.to_records(), # to_records() 将DF转换成numpy record数组 columns=pd.Index(['realgdp', 'infl', 'unemp'], name='item'), index=periods.to_timestamp('D', 'end')) print(data.head()) # result item realgdp infl unemp date 1959-03-31 2710.349 0.00 5.8 1959-06-30 2778.801 2.34 5.1 1959-09-30 2775.488 2.74 5.3 1959-12-31 2785.204 0.27 5.6 1960-03-31 2847.699 2.31 5.2
# 将宽格式转换为长格式 # 轴向旋转 -> 重置索引 -> rename列名 long_data = data.stack().reset_index().rename(columns={0: 'value'}) print(long_data.head()) # result date item value 0 1959-03-31 realgdp 2710.349 1 1959-03-31 infl 0.000 2 1959-03-31 unemp 5.800 3 1959-06-30 realgdp 2778.801 4 1959-06-30 infl 2.340
# 将长格式转换为宽格式 """ pd.pivot() 基于index/column的值重新调整DataFrame的坐标轴。不支持数据聚合,重复值会导致重复记录 语法格式: df.pivot(index(optional), columns, values) """ wide_data = long_data.pivot('date', 'item', 'value') print(wide_data.head()) # result item infl realgdp unemp date 1959-03-31 0.00 2710.349 5.8 1959-06-30 2.34 2778.801 5.1 1959-09-30 2.74 2775.488 5.3 1959-12-31 0.27 2785.204 5.6 1960-03-31 2.31 2847.699 5.2
# 增加一列value2 long_data['value2'] = np.random.rand(len(long_data)) print(long_data.head()) # result date item value value2 0 1959-03-31 realgdp 2710.349 0.155924 1 1959-03-31 infl 0.000 0.340776 2 1959-03-31 unemp 5.800 0.615475 3 1959-06-30 realgdp 2778.801 0.417256 4 1959-06-30 infl 2.340 0.845293 # 转换时如果不指定values,会将剩余的列都作为values列 pivoted = long_data.pivot('date', 'item') # data为index,item为columns print(pivoted.head()) # result value value2 item infl realgdp unemp infl realgdp unemp date 1959-03-31 0.00 2710.349 5.8 0.340776 0.155924 0.615475 1959-06-30 2.34 2778.801 5.1 0.845293 0.417256 0.825615 1959-09-30 2.74 2775.488 5.3 0.413700 0.512401 0.874806 1959-12-31 0.27 2785.204 5.6 0.081047 0.358632 0.790962 1960-03-31 2.31 2847.699 5.2 0.833500 0.395999 0.329820
5. 删除重复数据: data = pd.DataFrame({'k1': ['one'] * 3 + ['two'] * 4, 'k2': [1, 1, 2, 3, 3, 4, 4]}) print(data) # result k1 k2 0 one 1 1 one 1 2 one 2 3 two 3 4 two 3 5 two 4 6 two 4 # 判断当前行与前一行是否相同 print(data.duplicated()) # result 0 False 1 True 2 False 3 False 4 True 5 False 6 True dtype: bool # drop重复行 print(data.drop_duplicates()) # result k1 k2 0 one 1 2 one 2 3 two 3 5 two 4
# 新增v1列 data['v1'] = range(7) # 只以k1列为标准删除重复行 print(data.drop_duplicates(['k1'])) # result k1 k2 v1 0 one 1 0 3 two 3 3 # 以k1,k2为准,并且取最后一行的值 print(data.drop_duplicates(['k1', 'k2'], keep='last')) # result k1 k2 v1 1 one 1 1 2 one 2 2 4 two 3 4 6 two 4 6
6.利用函数及映射进行转换 # 使用字典映射进行转换 data = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon', 'Pastrami', 'corned beef', 'Bacon', 'pastrami', 'honey ham', 'nova lox'], 'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]}) print(data) # result food ounces 0 bacon 4.0 1 pulled pork 3.0 2 bacon 12.0 3 Pastrami 6.0 4 corned beef 7.5 5 Bacon 8.0 6 pastrami 3.0 7 honey ham 5.0 8 nova lox 6.0 meat_to_animal = { 'bacon': 'pig', 'pulled pork': 'pig', 'pastrami': 'cow', 'corned beef': 'cow', 'honey ham': 'pig', 'nova lox': 'salmon' } data['animal'] = data['food'].map(str.lower).map(meat_to_animal) print(data) # result food ounces animal 0 bacon 4.0 pig 1 pulled pork 3.0 pig 2 bacon 12.0 pig 3 Pastrami 6.0 cow 4 corned beef 7.5 cow 5 Bacon 8.0 pig 6 pastrami 3.0 cow 7 honey ham 5.0 pig 8 nova lox 6.0 salmon
# 使用lambda匿名函数进行转换 data['animal2'] = data.food.map(lambda x:meat_to_animal[x.lower()]) print(data) # result food ounces animal animal2 0 bacon 4.0 pig pig 1 pulled pork 3.0 pig pig 2 bacon 12.0 pig pig 3 Pastrami 6.0 cow cow 4 corned beef 7.5 cow cow 5 Bacon 8.0 pig pig 6 pastrami 3.0 cow cow 7 honey ham 5.0 pig pig 8 nova lox 6.0 salmon salmon

7.数据标准化
有时候由于量纲(数据单位)不一致,导致数据的差异很大,无法进行比较,需要进行数据标准化,将数据进行一定范围的压缩,以便进行数据比对等后续操作。 datafile = './data/normalization_data.xls' data = pd.read_excel(datafile, header=None) print(data) # result 0 1 2 3 0 78 521 602 2863 1 144 -600 -521 2245 2 95 -457 468 -1283 3 69 596 695 1054 4 190 527 691 2051 5 101 403 470 2487 6 146 413 435 2571 # 最小-最大规范化 data1 = (data - data.min()) / (data.max() - data.min()) print(data1) # result 0 1 2 3 0 0.074380 0.937291 0.923520 1.000000 1 0.619835 0.000000 0.000000 0.850941 2 0.214876 0.119565 0.813322 0.000000 3 0.000000 1.000000 1.000000 0.563676 4 1.000000 0.942308 0.996711 0.804149 5 0.264463 0.838629 0.814967 0.909310 6 0.636364 0.846990 0.786184 0.929571 # 零-均值规范化 data2 = (data - data.mean()) / data.std() print(data2) # result 0 1 2 3 0 -0.905383 0.635863 0.464531 0.798149 1 0.604678 -1.587675 -2.193167 0.369390 2 -0.516428 -1.304030 0.147406 -2.078279 3 -1.111301 0.784628 0.684625 -0.456906 4 1.657146 0.647765 0.675159 0.234796 5 -0.379150 0.401807 0.152139 0.537286 6 0.650438 0.421642 0.069308 0.595564 # np.ceil() 正向取整 data3 = data/10**np.ceil(np.log10(data.abs().max())) print(data3) # result 0 1 2 3 0 0.078 0.521 0.602 0.2863 1 0.144 -0.600 -0.521 0.2245 2 0.095 -0.457 0.468 -0.1283 3 0.069 0.596 0.695 0.1054 4 0.190 0.527 0.691 0.2051 5 0.101 0.403 0.470 0.2487 6 0.146 0.413 0.435 0.2571

8.replace替换 data = pd.Series([1., -999., 2., -999., -1000., 3.]) print(data) # result 0 1.0 1 -999.0 2 2.0 3 -999.0 4 -1000.0 5 3.0 dtype: float64 # 基本替换方式 print(data.replace(-999, np.nan)) # result 0 1.0 1 NaN 2 2.0 3 NaN 4 -1000.0 5 3.0 dtype: float64 # 使用列表分别替换对应位置的元素 print(data.replace([-999, -1000], [np.nan, 0])) # result 0 1.0 1 NaN 2 2.0 3 NaN 4 0.0 5 3.0 dtype: float64 # 使用字典进行更明确的替换 print(data.replace({-999: np.nan, -1000: 0})) # result 0 1.0 1 NaN 2 2.0 3 NaN 4 0.0 5 3.0 dtype: float64

9.重命名轴索引: data = pd.DataFrame(np.arange(12).reshape((3, 4)), index=['Ohio', 'Colorado', 'New York'], columns=['one', 'two', 'three', 'four']) data.index = data.index.map(str.upper) print(data) # result one two three four OHIO 0 1 2 3 COLORADO 4 5 6 7 NEW YORK 8 9 10 11 # 重命名索引及列名 print(data.rename(index=str.title, columns=str.upper)) # result ONE TWO THREE FOUR Ohio 0 1 2 3 Colorado 4 5 6 7 New York 8 9 10 11 # 使用字典映射新索引及新列名 print(data.rename(index={'OHIO': 'INDIANA'}, columns={'three': 'peekaboo'})) # result one two peekaboo four INDIANA 0 1 2 3 COLORADO 4 5 6 7 NEW YORK 8 9 10 11

10.数据离散化与面元划分 ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32] bins = [18, 25, 35, 60, 100] # 按照bins中的区间划分ages中的元素 cats = pd.cut(ages, bins) print(cats) # result [(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]] Length: 12 Categories (4, interval[int64]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]] # 查看元素属于哪个区间 print(cats.labels) # python2用法 print(cats.codes) # python3用法 # result [0 0 0 ..., 2 2 1] # 统计元素分布情况 print(pd.value_counts(cats)) # result (18, 25] 5 (35, 60] 3 (25, 35] 3 (60, 100] 1 dtype: int64
# 默认的区间访问为左开右闭,指定right=False后,变成左闭右开 print(pd.cut(ages, [18, 26, 36, 61, 100], right=False)) # result [[18, 26), [18, 26), [18, 26), [26, 36), [18, 26), ..., [26, 36), [61, 100), [36, 61), [36, 61), [26, 36)] Length: 12 Categories (4, interval[int64]): [[18, 26) < [26, 36) < [36, 61) < [61, 100)] # 手动设置标签,用来替换默认的区间 group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior'] cat2 = pd.cut(ages, bins, labels=group_names) print(cat2.value_counts()) # result MiddleAged 3 Senior 1 YoungAdult 3 Youth 5 dtype: int64
# 指定区间的划分精度 data = np.random.rand(20) print(pd.cut(data, 4, precision=2)) # result [(0.054, 0.27], (0.71, 0.93], (0.27, 0.49], (0.27, 0.49], (0.054, 0.27], ..., (0.71, 0.93], (0.71, 0.93], (0.71, 0.93], (0.054, 0.27], (0.71, 0.93]] Length: 20 Categories (4, interval[float64]): [(0.054, 0.27] < (0.27, 0.49] < (0.49, 0.71] < (0.71, 0.93]]
# 自定义分位点 print(pd.qcut(data, [0, 0.1, 0.5, 0.9, 1])) # result [(0.0953, 0.431], (0.893, 0.929], (0.431, 0.893], (0.0953, 0.431], (0.0953, 0.431], ..., (0.431, 0.893], (0.431, 0.893], (0.431, 0.893], (0.0536, 0.0953], (0.431, 0.893]] Length: 20 Categories (4, interval[float64]): [(0.0536, 0.0953] < (0.0953, 0.431] < (0.431, 0.893] < (0.893, 0.929]]

11.排列与随机采样
# np.random.permutation() df = pd.DataFrame(np.arange(5 * 4).reshape((5, 4))) print(df) # result 0 1 2 3 0 0 1 2 3 1 4 5 6 7 2 8 9 10 11 3 12 13 14 15 4 16 17 18 19 # 随机取5个数组成一个排列 sampler = np.random.permutation(5) print(sampler) # result [0 1 2 4 3] # 按照排列获取df中的数据 print(df.take(sampler)) # result 0 1 2 3 0 0 1 2 3 1 4 5 6 7 2 8 9 10 11 4 16 17 18 19 3 12 13 14 15 # 只取排列中的后三行数据 print(df.take(np.random.permutation(len(df))[:3])) # result 0 1 2 3 1 4 5 6 7 4 16 17 18 19 0 0 1 2 3
# np.random.randint() bag = np.array([5, 7, -1, 6, 4]) # 从0到5中随机取10个数 sampler = np.random.randint(0, len(bag), size=10) print(sampler) # result [4 0 0 3 3 4 3 0 1 1] # 将sampler作为索引值,获取bag的对应元素 draws = bag.take(sampler) print(draws) print(bag[sampler]) # 简化写法,可得同样结果 # result [4 5 5 6 6 4 6 5 7 7]

12.哑向量的使用
哑向量通常用来表示一组彼此间相互独立的属性,也成为因子。将他们的关系用只有0和1的向量表示,就叫做哑向量。
# 对某列取哑向量 df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'], 'data1': range(6), 'data2': [1, 3, 5, 7, 9, 11]}) print(pd.get_dummies(df['key'])) # result a b c 0 0 1 0 1 0 1 0 2 1 0 0 3 0 0 1 4 1 0 0 5 0 1 0 print(pd.get_dummies(df['data2'])) # result 1 3 5 7 9 11 0 1 0 0 0 0 0 1 0 1 0 0 0 0 2 0 0 1 0 0 0 3 0 0 0 1 0 0 4 0 0 0 0 1 0 5 0 0 0 0 0 1
# 对列名加前缀 dummies = pd.get_dummies(df['key'], prefix='key') # 将哑向量与df[data1]连接在一起 df_with_dummy = df[['data1']].join(dummies) print(df_with_dummy) # result data1 key_a key_b key_c 0 0 0 1 0 1 1 0 1 0 2 2 1 0 0 3 3 0 0 1 4 4 1 0 0 5 5 0 1 0
# 哑向量例子 # 读入影评数据 movies = pd.read_table('./data/movies.dat', sep='::', header=None, names=mnames) 数据文件内容: # 设置列名 mnames = ['movie_id', 'title', 'genres'] # 提取genres列中的数据,将分离的元素组成集合 genre_iter = (set(x.split('|')) for x in movies.genres) # 对genre_iter中的set集合解压后去重,再排序 genres = sorted(set.union(*genre_iter)) # 生成DataFrame哑向量 dummies = pd.DataFrame(np.zeros((len(movies), len(genres))), columns=genres) # 先根据数据文件生成一个元素均为0的DF for i, gen in enumerate(movies.genres): # 对genres进行循环 dummies.loc[i, gen.split('|')] = 1 # 将genres中的项按照行号设置为1,使其成为哑向量 # 将哑向量df与原df合并到一起 movies_windic = movies.join(dummies.add_prefix('Genre_')) # 查看第一行数据(Series格式) print(movies_windic.iloc[0]) # result movie_id 1 title Toy Story (1995) genres Animation|Children's|Comedy Genre_Action 0 Genre_Adventure 0 Genre_Animation 1 Genre_Children's 1 Genre_Comedy 1 Genre_Crime 0 Genre_Documentary 0 Genre_Drama 0 Genre_Fantasy 0 Genre_Film-Noir 0 Genre_Horror 0 Genre_Musical 0 Genre_Mystery 0 Genre_Romance 0 Genre_Sci-Fi 0 Genre_Thriller 0 Genre_War 0 Genre_Western 0 Name: 0, dtype: object
# 使用pd.cut()进行分类,然后转换成哑向量 values = np.random.rand(10) bins = [0, 0.2, 0.4, 0.6, 0.8, 1] pd.get_dummies(pd.cut(values, bins)) # result (0.0, 0.2] (0.2, 0.4] (0.4, 0.6] (0.6, 0.8] (0.8, 1.0] 0 1 0 0 0 0 1 0 0 0 1 0 2 0 0 0 0 1 3 0 0 1 0 0 4 1 0 0 0 0 5 0 0 0 1 0 6 0 0 1 0 0 7 0 0 0 0 1 8 0 1 0 0 0 9 0 1 0 0 0
人工智能
2018-10-18 14:24:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言
在本文开始前,作者并没有提倡LSTM是一种高度可靠的模型,它可以很好地利用股票数据中的内在模式,或者可以在没有任何人参与的情况下使用。写这篇文章,纯粹是出于对机器学习的热爱。在我看来,该模型已经观察到了数据中的某些模式,因此它可以在大多数时候正确预测股票的走势。但是,这个模型是否可以用于实际,有待用更多回测和实践去验证。

为什么需要时间序列模型?
你想要正确地模拟股票价格,因此作为股票买家,你可以合理地决定什么时候买股票,什么时候卖股票。这就是时间序列建模的切入点。你需要良好的机器学习模型,可以查看数据序列的历史记录,并正确地预测序列的未来元素是什么。

提示:股市价格高度不可预测且不稳定。这意味着在数据中没有一致的模式可以让你近乎完美地模拟股票价格。就像普林斯顿大学经济学家Burton Malkiel在他1973年的书中写到的:“随机漫步华尔街”,如果市场确实是有效的,那么当股票价格反应的所有因素一旦被公开时,那我们闭着眼睛都可以做的和专业投资者一样好。

但是,我们不要一直相信这只是一个随机过程,觉得机器学习是没有希望的。你不需要预测未来的股票确切的价格,而是股票价格的变动。做到这点就很不错了!

数据准备
使用以下数据源:
地址:https://www.kaggle.com/borismarjanovic/price-volume-data-for-all-us-stocks-etfs
当然你也可基于Wind数据库去研究。因为Wind数据相对于其他平台和数据商而言,总体上在国内算是比较全面和准确的。

从Kaggle获得数据
在Kaggle上找到的数据是csv文件,所以,你不需要再进行任何的预处理,因此你可以直接将数据加载到DataFrame中。同时你还应该确保数据是按日期排序的,因为数据的顺序在时间序列建模中至关重要。 df = df.sort_values('Date') df.head()

数据可视化 plt.figure(figsize = (18,9)) plt.plot(range(df.shape[0]),(df['Low']+df['High'])/2.0) plt.xticks(range(0,df.shape[0],500),df['Date'].loc[::500],rotation=45) plt.xlabel('Date',fontsize=18) plt.ylabel('Mid Price',fontsize=18) plt.show()
上图已经说明了很多东西。我选择这家公司而不是其他公司的具体原因是,随着时间的推移,这张图中展现了不同的股价行为。这将使学习更加稳健,并且可以更改以便测试各种情况下预测的好坏程度。

数据拆分训练集和测试集
计算一天中最高和最低价的平均值来计算的中间价格。 high_prices = df.loc[:,'High'].as_matrix() low_prices = df.loc[:,'Low'].as_matrix() mid_prices = (high_prices+low_prices)/2.0

现在你可以分离训练数据和测试数据。训练数据是时间序列的前11000个数据,其余的是测试数据。 train_data = mid_prices[:11000] test_data = mid_prices[11000:]

现在需要定义一个标准对数据进行归一化。MinMaxScalar方法将所有数据归到0和1之间。你还可以将训练和测试数据重新组为[data size, num features]。 scaler = MinMaxScaler() train_data = train_data.reshape(-1,1) test_data = test_data.reshape(-1,1)

根据之前得数据,可以看出不同时间段有不同的取值范围,你可以将整个序列拆分为窗口来进行归一化。如果不这样做,早期的数据接近于0,并且不会给学习过程增加太多价值。这里你选择的窗口大小是2500。

当选择窗口大小时,确保它不是太小,因为当执行窗口规范化时,它会在每个窗口的末尾引入一个中断,因为每个窗口都是**规范化的。

在本例中,4个数据点将受此影响。但假设你有11000个数据点,4个点不会引起任何问题。 smoothing_window_size = 2500 for di in range(0,10000,smoothing_window_size): scaler.fit(train_data[di:di+smoothing_window_size,:]) train_data[di:di+smoothing_window_size,:] = scaler.transform(train_data[di:di+smoothing_window_size,:]) # You normalize the last bit of remaining data scaler.fit(train_data[di+smoothing_window_size:,:]) train_data[di+smoothing_window_size:,:] = scaler.transform(train_data[di+smoothing_window_size:,:])

将数据重新塑造为[data_size]的Shape: train_data = train_data.reshape(-1) test_data = scaler.transform(test_data).reshape(-1)

现在可以使用指数移动平均平滑数据。可以帮助你避免股票价格数据的杂乱,并产生更平滑的曲线。我们只使用训练数据来训练MinMaxScaler,通过将MinMaxScaler与测试数据进行匹配来规范化测试数据是错误的。

注意:你应该只平滑训练数据。 EMA = 0.0 gamma = 0.1 for ti in range(11000): EMA = gamma*train_data[ti] + (1-gamma)*EMA train_data[ti] = EMA all_mid_data = np.concatenate([train_data,test_data],axis=0)
下面是平均结果。它非常接近股票的实际行为。接下来您将看到一个更精确的一步预测方法:
上面的图(和MSE)说明了什么呢?对于非常短的predictiosn(一天之后)来说,这个模型似乎不算太坏。考虑到股票价格在一夜之间不会从0变化到100,这种行为是明智的。接下来我们来看一种更有趣的平均技术,称为指数移动平均。

指数移动平均线
你可能在互联网上看到过一些文章使用非常复杂的模型来预测股票市场的行为。但是要小心!我所看到的这些都只是视觉错觉,不是因为学习了有用的东西。下面你将看到如何使用简单的平均方法复制这种行为。 window_size = 100 N = train_data.size run_avg_predictions = [] run_avg_x = [] mse_errors = [] running_mean = 0.0 run_avg_predictions.append(running_mean) decay = 0.5 for pred_idx in range(1,N): running_mean = running_mean*decay + (1.0-decay)*train_data[pred_idx-1] run_avg_predictions.append(running_mean) mse_errors.append((run_avg_predictions[-1]-train_data[pred_idx])**2) run_avg_x.append(date) print('MSE error for EMA averaging: %.5f'%(0.5*np.mean(mse_errors)))
MSE error for EMA averaging: 0.00003

如果指数移动平均线很好,为什么需要更好的模型呢?

可以看到,它符合遵循真实分布的完美直线(通过非常低的MSE证明了这一点)。实际上,仅凭第二天的股票市值,你就做不了什么。就我个人而言,我想要的不是第二天股市的确切价格,而是未来30天股市的价格会上涨还是下跌

让我们试着在窗口中进行预测(假设你预测接下来两天的窗口,而不是第二天)。然后你就会意识到EMA会有多么的失败。让我们通过一个例子来理解这一点。 不管你预测未来的步骤是多少,你都会得到相同的答案。

输出有用信息的一种解决方案是查看基于动量算法。他们的预测是基于过去的近期值是上升还是下降(而不是精确的数值)。例如,如果过去几天的价格一直在下降,第二天的价格可能会更低。这听起来很合理。然而,我们将使用更复杂的模型:LSTM。

评价结果
我们将使用均值平方误差来计算我们的模型有多好。均值平方误差(MSE)的计算方法是先计算真实值与预测值之间的平方误差,然后对所有的预测进行平均。但是: 平均预测是一种很好的预测方法(这对股票市场的预测不是很有用),但对未来的预测并不是很有用。

LSTM简介
长短时记忆模型是非常强大的时间序列模型。它们可以预测未来任意数量的步骤。LSTM模块(或单元)有5个基本组件,可以对长期和短期数据进行建模。

LSTM单元格如下所示:

计算方程如下:
Tensorflow为实现时间序列模型提供了一个很好的子API。后面我们会使用到它。

LSTM数据生成器
首先要实现一个数据生成器来训练LSTM。这个数据生成器将有一个名为unroll batch(…)的方法,该方法将输出一组按顺序批量获取num unrollings的输入数据,其中批数据的大小为[batch_size, 1]。然后每批输入数据都有相应的输出数据。

例如,如果num unrollings=3和batch size=4则看起来像一组展开的批次。
输入数据: [x0,x10,x20,x30],[x1,x11,x21,x31],[x2,x12,x22,x32]
输出数据: [x1,x11,x21,x31],[x2,x12,x22,x32],[x3,x13,x23,x33]

数据生成器
下面将演示如何可视化创建一批数据。基本思想是将数据序列划分为N / b段,使每个段的大小为b,然后定义游标每段为1。然后对单个数据进行抽样,我们得到一个输入(当前段游标索引)和一个真实预测(在[当前段游标+1,当前段游标+5]之间随机抽样)。请注意,我们并不总是得到输入旁边的值,就像它的预测一样。这是一个减少过拟合的步骤。在每次抽样结束时,我们将光标增加1。

定义超参数
在本节中,将定义几个超参数。D是输入的维数。很简单,你以之前的股票价格作为输入并预测下一个应该为1。然后是num unrollings,它表示单个优化步骤需要考虑多少连续时间步骤。越大越好。然后是batch size。批量处理大小是在单个时间步骤中考虑的数据样本的数量。越大越好,因为在给定的时间内数据的可见性越好。接下来定义num_nodes,它表示每个单元格中隐藏的神经元数量。在这个示例中,你可以看到有三层LSTM。 D = 1 num_unrollings = 50 batch_size = 500 num_nodes = [200,200,150] n_layers = len(num_nodes) dropout = 0.2 tf.reset_default_graph()

定义输入和输出
接下来为训练输入和标签定义占位符。这非常简单,因为你有一个输入占位符列表,其中每个占位符包含一批数据。 该列表包含num_unrollings占位符,它将用于单个优化步骤。 train_inputs, train_outputs = [],[] for ui in range(num_unrollings): train_inputs.append(tf.placeholder(tf.float32, shape=[batch_size,D],name='train_inputs_%d'%ui)) train_outputs.append(tf.placeholder(tf.float32, shape=[batch_size,1], name = 'train_outputs_%d'%ui))

定义LSTM和回归层的参数
用三个LSTM层和一个线性回归层,用w和b表示,该层提取最后一个长短期内**元的输出并输出对下一个时间步骤的预测。你可以使用TensorFlow中的MultiRNNCell来封装创建的三个LSTMCell对象。此外,还可以使用dropout实现LSTM单元格,因为它们可以提高性能并减少过拟合。 lstm_cells = [ tf.contrib.rnn.LSTMCell(num_units=num_nodes[li], state_is_tuple=True, initializer= tf.contrib.layers.xavier_initializer() ) for li in range(n_layers)] drop_lstm_cells = [tf.contrib.rnn.DropoutWrapper( lstm, input_keep_prob=1.0,output_keep_prob=1.0-dropout, state_keep_prob=1.0-dropout ) for lstm in lstm_cells] drop_multi_cell = tf.contrib.rnn.MultiRNNCell(drop_lstm_cells) multi_cell = tf.contrib.rnn.MultiRNNCell(lstm_cells) w = tf.get_variable('w',shape=[num_nodes[-1], 1], initializer=tf.contrib.layers.xavier_initializer()) b = tf.get_variable('b',initializer=tf.random_uniform([1],-0.1,0.1))


计算LSTM输出并将其输入回归层,得到最终预测结果
首先创建TensorFlow变量(c和h),它将保持单元状态和长短期记忆单元的隐藏状态。 然后将train_input列表转换为[num_unrollings, batch_size, D],使用tf.nn.dynamic_rnn计算所需输出。然后使用tf.nn.dynamic_rnn计算LSTM输出。并将输出分解为一列num_unrolling的张量。预测和真实股价之间的损失。 c, h = [],[] initial_state = [] for li in range(n_layers): c.append(tf.Variable(tf.zeros([batch_size, num_nodes[li]]), trainable=False)) h.append(tf.Variable(tf.zeros([batch_size, num_nodes[li]]), trainable=False)) initial_state.append(tf.contrib.rnn.LSTMStateTuple(c[li], h[li])) all_inputs = tf.concat([tf.expand_dims(t,0) for t in train_inputs],axis=0) all_lstm_outputs, state = tf.nn.dynamic_rnn( drop_multi_cell, all_inputs, initial_state=tuple(initial_state), time_major = True, dtype=tf.float32) all_lstm_outputs = tf.reshape(all_lstm_outputs, [batch_size*num_unrollings,num_nodes[-1]]) all_outputs = tf.nn.xw_plus_b(all_lstm_outputs,w,b) split_outputs = tf.split(all_outputs,num_unrollings,axis=0)


损失计算和优化器
现在,要计算损失。然而,在计算损失时,你应该注意到有一个独特的特征。对于每一批预测和真实输出,计算均方误差。然后把所有这些均方损失加起来(不是平均值)。最后,定义要用来优化神经网络的优化器在这种情况下,您可以使用Adam,这是一个非常新且性能良好的优化器。 print('Defining training Loss') loss = 0.0 with tf.control_dependencies([tf.assign(c[li], state[li][0]) for li in range(n_layers)]+ [tf.assign(h[li], state[li][1]) for li in range(n_layers)]): for ui in range(num_unrollings): loss += tf.reduce_mean(0.5*(split_outputs[ui]-train_outputs[ui])**2) print('Learning rate decay operations') global_step = tf.Variable(0, trainable=False) inc_gstep = tf.assign(global_step,global_step + 1) tf_learning_rate = tf.placeholder(shape=None,dtype=tf.float32) tf_min_learning_rate = tf.placeholder(shape=None,dtype=tf.float32) learning_rate = tf.maximum( tf.train.exponential_decay(tf_learning_rate, global_step, decay_steps=1, decay_rate=0.5, staircase=True), tf_min_learning_rate) # Optimizer. print('TF Optimization operations') optimizer = tf.train.AdamOptimizer(learning_rate) gradients, v = zip(*optimizer.compute_gradients(loss)) gradients, _ = tf.clip_by_global_norm(gradients, 5.0) optimizer = optimizer.apply_gradients( zip(gradients, v)) print('\tAll done')
这里定义了与预测相关的TensorFlow操作。首先,为输入(sample_inputs)定义一个占位符,然后与训练阶段类似,定义预测的状态变量(sample_c和sample_h)。最后用tf.nn.dynamic_rnn计算预测。后通过回归层(w和b)发送输出。 还应该定义reset_sample_state操作,该操作将重置单元状态和隐藏状态。 每次进行一系列预测时,都应该在开始时执行此操作。
print('Defining prediction related TF functions') sample_inputs = tf.placeholder(tf.float32, shape=[1,D]) sample_c, sample_h, initial_sample_state = [],[],[] for li in range(n_layers): sample_c.append(tf.Variable(tf.zeros([1, num_nodes[li]]), trainable=False)) sample_h.append(tf.Variable(tf.zeros([1, num_nodes[li]]), trainable=False)) initial_sample_state.append(tf.contrib.rnn.LSTMStateTuple(sample_c[li],sample_h[li])) reset_sample_states = tf.group(*[tf.assign(sample_c[li],tf.zeros([1, num_nodes[li]])) for li in range(n_layers)], *[tf.assign(sample_h[li],tf.zeros([1, num_nodes[li]])) for li in range(n_layers)]) sample_outputs, sample_state = tf.nn.dynamic_rnn(multi_cell, tf.expand_dims(sample_inputs,0), initial_state=tuple(initial_sample_state), time_major = True, dtype=tf.float32) with tf.control_dependencies([tf.assign(sample_c[li],sample_state[li][0]) for li in range(n_layers)]+ [tf.assign(sample_h[li],sample_state[li][1]) for li in range(n_layers)]): sample_prediction = tf.nn.xw_plus_b(tf.reshape(sample_outputs,[1,-1]), w, b) print('\tAll done')


运行LSTM
在这里,你将训练和预测几个时期的股票价格走势,看看这些预测是否会随着时间的推移而变得更好或更糟。按照以下步骤操作: 在时间序列上定义一组测试起点(test_points_seq)来计算LSTM 对于每一个epoch 用于训练数据的完整序列长度 展开一组num_unrollings批次 使用展开的批次LSTM进行训练 计算平均训练损失 对于测试集中的每个起点 通过迭代在测试点之前找到的以前的num_unrollings数据点来更新LSTM状态 使用先前的预测作为当前输入,连续预测n_predict_once步骤 计算预测到的n_predict_once点与当时股票价格之间的MSE损失

部分代码 epochs = 30 valid_summary = 1 n_predict_once = 50 train_seq_length = train_data.size train_mse_ot = [] test_mse_ot = [] predictions_over_time = [] session = tf.InteractiveSession() tf.global_variables_initializer().run() loss_nondecrease_count = 0 loss_nondecrease_threshold = 2 print('Initialized') average_loss = 0 data_gen = DataGeneratorSeq(train_data,batch_size,num_unrollings) x_axis_seq = [] test_points_seq = np.arange(11000,12000,50).tolist() for ep in range(epochs): # ========================= Training ===================================== for step in range(train_seq_length//batch_size): u_data, u_labels = data_gen.unroll_batches() feed_dict = {} for ui,(dat,lbl) in enumerate(zip(u_data,u_labels)): feed_dict[train_inputs[ui]] = dat.reshape(-1,1) feed_dict[train_outputs[ui]] = lbl.reshape(-1,1) feed_dict.update({tf_learning_rate: 0.0001, tf_min_learning_rate:0.000001}) _, l = session.run([optimizer, loss], feed_dict=feed_dict)


可视化预测
可以看到MSE损失是如何随着训练量的减少而减少的。这是一个好迹象,表明模型正在学习一些有用的东西。你可以看到LSTM比标准平均值做得更好。标准平均(虽然不完美)合理地跟随真实的股票价格运动。 best_prediction_epoch = 28 plt.figure(figsize = (18,18)) plt.subplot(2,1,1) plt.plot(range(df.shape[0]),all_mid_data,color='b') predictions with high alpha start_alpha = 0.25 alpha = np.arange(start_alpha,1.1,(1.0-start_alpha)/len(predictions_over_time[::3])) for p_i,p in enumerate(predictions_over_time[::3]): for xval,yval in zip(x_axis_seq,p): plt.plot(xval,yval,color='r',alpha=alpha[p_i]) plt.title('Evolution of Test Predictions Over Time',fontsize=18) plt.xlabel('Date',fontsize=18) plt.ylabel('Mid Price',fontsize=18) plt.xlim(11000,12500) plt.subplot(2,1,2) plt.plot(range(df.shape[0]),all_mid_data,color='b') for xval,yval in zip(x_axis_seq,predictions_over_time[best_prediction_epoch]): plt.plot(xval,yval,color='r') plt.title('Best Test Predictions Over Time',fontsize=18) plt.xlabel('Date',fontsize=18) plt.ylabel('Mid Price',fontsize=18) plt.xlim(11000,12500) plt.show()
尽管LSTM并不完美,但它似乎在大多数情况下都能正确预测股价走势。请注意,你的预测大致在0和1之间(也就是说,不是真实的股票价格)。这是可以的,因为你预测的是股价的走势,而不是股价本身。

结论
股票价格/移动预测是一项极其困难的任务。就我个人而言,我认为任何股票预测模型都不应该被视为理所当然,并且盲目地依赖它们。然而,模型在大多数情况下可能能够正确预测股票价格的变动,但并不总是如此。

不要被那些预测曲线完全与真实股价重叠的文章所迷惑。这可以用一个简单的平均技术来复制,但实际上它是无用的。更明智的做法是预测股价走势。

模型的超参数对你得到的结果非常敏感。因此,一个非常好的事情是在超参数上运行一些超参数优化技术(例如,网格搜索/随机搜索)。这里列出了一些最关键的超参数:优化器的学习率、层数、每层的隐藏单元数,优化器Adam表现最佳,模型的类型(GRU / LSTM / LSTM with peepholes)。

由于本文由于数据量小,我们用测试损耗来衰减学习速率。这间接地将测试集的信息泄露到训练过程中。处理这个问题更好的方法是有一个单独的验证集(除了测试集)与验证集性能相关的衰减学习率。

延伸阅读:
使用Tensorflow预测股票市场变动
人工智能
2018-10-18 11:04:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
背景
深度学习作为AI时代的核心技术,已经被应用于多个场景。在系统设计层面,由于其具有计算密集型的特性,所以与传统的机器学习算法在工程实践过程中存在诸多的不同。本文将介绍美团平台在应用深度学习技术的过程中,相关系统设计的一些经验。
本文将首先列举部分深度学习算法所需的计算量,然后再介绍为满足这些计算量,目前业界比较常见的一些解决方案。最后,我们将介绍美团平台在NLU和语音识别两个领域中,设计相关系统的经验。
深度学习的计算量
Model Input Size Param Size Flops AlexNet 227 x 227 233 MB 727 MFLOPs
CaffeNet 224 x 224 233 MB 724 MFLOPs
VGG-VD-16 224 x 224 528 MB 16 GFLOPs
VGG-VD-19 224 x 224 548 MB 20 GFLOPs
GoogleNet 224 x 224 51 MB 2 GFLOPs
ResNet-34
ResNet-152 SENet
224 x 224
224 x 224 224 x 224
83 MB
230 MB 440 MB
4 GFLOPs
11 GFLOPs 21 GFLOPs
数据来源 上表列举了,ImageNet图像识别中常见算法的模型大小以及单张图片一次训练(One Pass)所需要的计算量。
自2012年,Hinton的学生Alex Krizhevsky提出AlexNet,一举摘下ILSVRC 2012的桂冠后,ILSVRC比赛冠军的准确率越来越高。与此同时,其中使用到的深度学习算法也越来越复杂,所需要的计算量也越来越大。SENet与AlexNet相比,计算量多了近30倍。我们知道,ImageNet大概有120万张图片,以SENet为例,如果要完成100个epoch的完整训练,将需要2.52 * 10^18的计算量。如此庞大的计算量,已经远远超出传统的机器学习算法的范畴。更别说,Google在论文 《Revisiting Unreasonable Effectiveness of Data in Deep Learning Era》 中提及的、比ImageNet大300倍的数据集。
物理计算性能
面对如此庞大的计算量,那么,我们业界当前常用的计算单元的计算力是多少呢? CPU 物理核:一般浮点运算能力在10^10 FLOPS量级。一台16 Cores的服务器,大致上有200 GFLOPS的运算能力。实际运行,CPU 大概能用到80%的性能,那就160 GFLOPS的运算能力。完成上述SENet运行,需要182天。 NVIDIA GPGPU: 目前的V100,单精度浮点运算的峰值大概为14 TFLOPS, 实际运行中,我们假设能用到50%的峰值性能,那就是7 TFLOPS,需要4天。
根据以上数据结果可以看出:在深度学习领域,GPU训练数据集所需要耗费的时间,远远少于CPU,这也是当前深度学习训练都是采用GPU的重要原因。
业界的解决方案
从前面的计算可知,即使使用GPU来计算,训练一次ImageNet 也需要4天的时间。但对于算法工程师做实验、调参而言,这种耗时数天的等待是难以忍受的。为此,目前业界针对深度学习训练的加速,提出了各种各样的解决方案。
异构计算的并行方案
数据并行(Data Parallelism)
数据并行,即每个计算单元都保留一份完整的模型拷贝,分别训练不同的数据,经过一个Iteration或若干个Iteration后,把各个计算单元的模型做一次同步。这是最常见的深度学习训练方式,好处在于逻辑简单、代码实现方便。
模型并行(Model Parallelism)
模型并行,即各个计算单元存储同一层模型数据的不同部分,训练相同的数据。相对于数据并行,因为各个运算单元每训练完一层神经网络,就必须要同步一次,频繁的同步通信导致系统不能充分地利用硬件的运算能力,所以更为少见。但是在一些业务场景下,Softmax层需要分类的类别可能会有很多,导致Softmax层太大,单个计算单元无法存储,这个时候,需要把模型切割成若干部分,存储在不同的运算单元。模型并行常见于NLU、推荐、金融等领域。
流式并行(Stream Parallelism)
流式并行,即每个计算单元都存储不同层的模型数据,训练相同的数据。如上图所示,GPU1只负责第一层神经网络的计算,GPU2只负责2~5层神经网络的计算,GPU3只负责第6层的计算。流式并行的好处在于每个运算单元之间的通信和计算重叠(overlap),如果配置得当,可以非常充分地利用硬件资源。缺点在于,根据不同的模型,需要平衡好各个计算单元的计算量,如果配置不好,很容易形成“堰塞湖”。如上图所示,很有可能出现GPU1 负责的运算量太少,而GPU2 负责的运算量太多,导致GPU1 和GPU2 之间堵塞住大量的Mini-batch,更常见于线上环境。
混合并行(Hybrid Parallelism)
混合并行,即上面提到的并行方式的混合。如对于一些图像识别任务来说,可能前几层使用数据并行,最后的Softmax层,使用模型并行。
异构计算的硬件解决方案 单机单卡:一个主机内安装上一块GPU运算卡。常见于个人计算机。 单机多卡:一个主机内安装上多块GPU运算卡。常见的有:1机4卡,1机8卡,甚至有1机10卡。一般公司都采取这种硬件方案。 多机多卡:多台主机内安装多块GPU运算卡。常见于公司内部的计算集群,一般多机之间采取Infiniband 来实现网络的快速通信。 定制化:即类似于Google的TPU解决方案。常见于“巨无霸”公司内部。
异构计算的通信解决方案
根据上面的硬件解决方案,我们以ResNet为例:模型的大小为230M,单张图片运算量为11 GFLPOS,Mini-batch假设为128。可以计算出各个硬件模块在深度学习训练中的耗时比较: GPU:对于V100,假设有6 TFLOPS,一次Mini-batch 理论耗时:0.23s。 PCI-E:常见PCI-E 3.0 * 16,速度为10 GB/s,传输一个模型的理论耗时为:0.023s。 网络:假设为10 GB/s的高速网络,传输一个模型的理论耗时:0.023s。 Disk:普通的磁盘,我们假设200M/s的读取速度,读取一次Mini-batch所需要的图片耗时:0.094s。
根据上面的数据结果,我们似乎可以得出一个结论:PCI-E和网络的传输耗时,相对于GPU来说,整整少了一个数量级,所以网络通信同步的时间可以忽略不计。然而问题并没有那么简单,上面例子中的耗时只是单个模型的耗时,但是对于8卡的集群来说,如果使用数据并行,每次同步就需要传输8份模型,这就导致数据传输的时间和GPU的计算时间“旗鼓相当”。这样的话,GPU就得每训练完一个Mini-batch,都得等候很久的一段时间(采取同步更新),这会浪费很多计算资源。因此,网络通信也需要制定对应的解决方案。下面我们以Nvidia NCCL中单机多卡的通信解决方案为例介绍,而多机多卡的通信解决方案其实是类似的。 上图是单机4卡机器,在硬件上,两种不同的通信体系。左边为普通的PCI-E通信,即4个GPU之间组成一个环状。右边为NVLink通信,即两两之间相互连接。 常见的通信类型如下图所示: 对于深度学习训练而言,关键的两种通信类型为:Broadcast和Reduce。Broadcast用于Master分发最新的模型给各个GPU。Reduce 用于各个GPU计算完Mini-batch后,把模型更新值汇总到Master上。以Broadcast为例,最简单的通信方式是Master往各个GPU上发送数据,这样的耗时就是4次模型传输的时间,通信时间就会太长,一种简单的优化方法如下图所示: 即把所需要传输的数据分成若干块,然后通过接力的方式逐个传递,每个GPU都把自己最新的一块数据发送到下一个GPU卡上。这种传输方式能充分利用硬件层面的通信结构,使得需要的耗时大幅缩减。与此类似的,Reduce的通信优化也可以采取相同的方式进行提速。
美团的定制化深度学习系统
尽管目前在业界已经推出了很多著名的深度学习训练平台,通用的训练平台如TensorFlow、MxNet等等,还有领域专用的训练平台,如语音识别中的Kaldi,但是我们经过调研后,决定内部自主开发一套深度学习系统,理由如下: 通用的训练平台,缺乏了领域特色的功能。如语音识别中的特征提取模块和算法。 通用的训练平台,通常是基于Data-flow Graph,来对计算图中的每个operator进行建模,所以颗粒度很小,需要调度的单元多,导任务调度复杂。 领域特色的训练平台,如Kaldi,在神经网络训练的时候,性能不足。 线上业务存在很多特殊性,如果使用TensorFlow之类作为训练平台,不太适合线上业务的情景。
NLU线上系统
线上系统的业务特点
我们在设计NLU线上系统时,考虑了NLU业务的一些特性。发现其具备如下的一些特点: 随着业务和技术的变化,算法流程也经常发生变化。 算法流程是多个算法串联组成的,不单纯的只有深度学习算法。如分词等算法就不是DL算法。 为了能够快速响应一些紧急问题,需要经常对模型进行热更新。 更重要的是,我们希望构建一个能以“数据驱动”的自动迭代闭环。
业务多变
NLU任务的算法流程是多层级的,并且业务经常发生变化。如下图所示: 即随着业务要求的变化,NLU系统一开始的算法流程,只需要把一个Query分为两个类,但是到后面,极有可能会变成需要分为三个类别。
热更新
根据业务需求,或者为了紧急处理一些特殊问题,NLU线上系统经常需要做出快速响应,热更新算法模型。如最近的热点词“skr”,几乎是一夜之间,突然火爆起来。如下图所示的微博,如果不能正确理解“skr”的正确语义,可能就不能准确理解这条微博想要表达的意思。 为了避免影响用户体验,我们可能会对NLU系统,马上进行热更新,把新模型紧急进行上线。
数据驱动的自动迭代闭环
对于线上系统而言,构建如上图所示的自动迭代闭环,能更好地利用业务数据来提升服务质量。
NLU线上系统的核心设计
算法流程的抽象
为了适应线上系统串联、多变的算法流程,我们把线上系统的算法进行抽象,如下图所示: 即每一个算法,都依赖于若干个槽位(Slot)和资源(Resource),一旦槽位和资源就位,就会触发对应的算法执行。算法的执行先通过算法适配器,来适配槽位和资源中的数据,转换成算子的输入格式。然后算子执行算法本身,执行完算子后,再经过算法解析器。算法解析器主要用于解析算法执行的结果,触发对应的槽位。如根据算法的结果,触发Top 3的结果。 多个算法串联起来,就构建成如下结果:
热更新流程的设计
如上图所示,我们把算法的热更新流程设计如上。初试状态为左上角,即多个Query使用同一份模型数据。当遇到模型更新的请求后,系统将会block住新的query(右上角状态)。然后更新模型完后,新的query使用新的模型,旧query依然使用旧模型(右下角状态)。最后,当使用旧模型的query结束后,把旧的模型从内存中删除(左下角),然后系统恢复到初始状态。
声学模型训练系统
因为TensorFlow等通用深度学习训练平台,缺乏了特征提取等业务相关的领域功能,而Kaldi的声学模型训练过程又太慢。所以美团开发了一个声学模型训练系统——Mimir,其具备如下特性: 使用比TensorFlow更粗颗粒度的建模单元,使得任务调度、优化更简单方便易行。 使用数据并行的并行方案,单机多卡可达到近线性加速。(采取同步更新策略下,4卡加速比达到3.8) 移植了Kaldi的一些特有的训练算法。 速度上为Kaldi的6~7倍。(800个小时的训练数据,单机单卡的条件下,Kaldi需要6~7天, Mimir只需20个小时) 业务上,移植了Kaldi的特征提取等领域的相关模块。
参考资料 NCCL: ACCELERATED MULTI-GPU COLLECTIVE COMMUNICATIONS 【深度学习】老师木讲架构:深度学习平台技术演进
作者简介
剑鹏,美团点评算法专家。2017年加入美团,目前作为语音识别团队的声学模型负责人,负责声学模型相关的算法和系统设计与开发。
人工智能
2018-10-26 09:57:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
这篇文章是系列文章的第1部分,第2部分将阐述AutoML和神经架构搜索、第3部分将特别地介绍Google的AutoML。
关于机器学习人才的稀缺和公司声称他们的产品能够自动化机器学习而且能完全消除对ML专业知识需求的承诺经常登上媒体的新闻头条。在TensorFlow DevSummit的主题演讲中,Google的AI总指挥Jeff Dean估计,有数千万拥有可用于机器学习的数据而缺乏必要的专业知识和技能的组织。因为我在fast.ai主要专注于让更多的人去使用机器学习并且让它更容易使用,所以我密切关注刚才所提的机器学习人才稀缺等问题。
在考虑如何使机器学习的一些工作自动化以及让具有更广泛背景的人更容易使用这项技术,首先有必要问的是:机器学习行业从业者到底在做什么?任何用来解决机器学习专业知识稀缺的方案都需要回答这个问题:我们是否知道去教什么技能、去建立什么工具或者去自动化什么工序。
从事机器学习行业的人做什么?
构建数据产品是一项复杂的工作

虽然许多关于机器学习的学术来源几乎都是预测建模,但这只是从事机器学习在正常情况下做的其中一件事。适当地分析商业问题,收集和清理数据,构建模型,实施结果,然后监控变化的过程在很多方式中是相互关联的,这往往很难仅仅通过单个部分进行孤立(至少不知道其他部分需要什么)。正如Jeremy Howard等人在《设计出色的数据产品》上写道:伟大的预测建模是解决方案的重要组成部分,但它不再独立;随着产品变得越来越复杂,它就会消失在管道中。
构建数据产品是一项复杂的工作
人工智能
2018-10-25 13:49:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
TP/TN/FP/FN的定义
TP: 预测为1(Positive),实际也为1(Truth-预测对了)
TN: 预测为0(Negative),实际也为0(Truth-预测对了)
FP: 预测为1(Positive),实际为0(False-预测错了)
FN: 预测为0(Negative),实际为1(False-预测错了)
Accuracy/Precision/Recall/F1的定义
Accuracy = (预测正确的样本数)/(总样本数)=(TP+TN)/(TP+TN+FP+FN)
Precision = (预测为1且正确预测的样本数)/(所有预测为1的样本数) = TP/(TP+FP)
Recall = (预测为1且正确预测的样本数)/(所有真实情况为1的样本数) = TP/(TP+FN)
F1 = 2*(Precision*Recall)/(Precision+Recall)
IOU的定义
IoU = TP/(TP+TN+FP)
人工智能
2018-10-25 11:48:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
问题描述 DL中我们可能会根据tensor中元素的值进行不同的操作(比如loss阶段会根据grandtruth或outputs中元素的大小进行不同的loss操作),这时就要对tensor中的元素进行判断。在python中可以用for + if语句进行判断。但TF中输入是Tensor,for和if语句失效。
tf.where说明 格式:tf.where(condition, x=None, y=None, name=None) 参数:
condition: 一个元素为bool型的tensor。元素内容为false,或true。
x: 一个和condition有相同shape的tensor,如果x是一个高维的tensor,x的第一维size必须和condition一样。
y: 和x有一样shape的tensor 返回:
一个和x,y有同样shape的tensor 功能:
遍历condition Tensor中的元素,如果该元素为true,则output Tensor中对应位置的元素来自x Tensor中对应位置的元素;否则output Tensor中对应位置的元素来自Y tensor中对应位置的元素。
使用例子
比如当tensor中元素x大于等于5时,对应输出tensor中元素y=x * 2;否则 y= x * 3的计算 import os import sys import tensorflow as tf import numpy as np # # y = x * 2 (x >= 5) # y = x * 3 (x < 5) # a = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9]) tmp = tf.constant([0, 0, 0, 0, 0, 0, 0, 0, 0]) condition = tf.less(a, 5) smaller = tf.where(condition, a, tmp) bigger = tf.where(condition, tmp, a) compute_smaller = smaller * 3 compute_bigger = bigger * 2 result = compute_smaller + compute_bigger with tf.Session() as sess: print(sess.run(result)) # # 结果: [ 3 6 9 12 10 12 14 16 18]
上述过程也可以改成如下 a = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9]) condition = tf.less(a, 5) result = tf.where(condition, a * 3, a * 2) with tf.Session() as sess: print(sess.run(result))
整个过程中核心部分是condition条件的设置,该条件可以用tf提供的less,equal等操作实现(详细查看tf文档)。
tmp变量为引入的一个临时变量,目的是为了保证where按条件选择后输出的Tensor大小不变(tmp的0元素在乘法中是无意义计算,用该方法保证未背选择的元素在smaller和bigger中不参与计算) 详细的condition 和 tmp需要根据实际的计算进行不同的设置
参考
https://stackoverflow.com/questions/42689342/compare-two-tensors-elementwise-tensorflow
https://stackoverflow.com/questions/37912161/how-can-i-compute-element-wise-conditionals-on-batches-in-tensorflow
人工智能
2018-10-23 17:09:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
什么是稀疏矩阵
将矩阵中的元素按照零和非零分为两个集合,对于零元素,其占整个矩阵的元素个数比成为稠密度。 稠密度 > 0.05 稠密矩阵 稠密度 < 0.05 稀疏矩阵
稀疏矩阵算法
通过压缩稀疏矩阵中的零元素,节省运算空间。 常用算法 BLAS sparse matrix (稀疏矩阵)algorithms
1. coordinate storage - coo
一个直接的思路是只存储非零元素。而 coo 是其中最简单的存储结构。
coo index strucutre: 三个稀疏向量 three sparse vectors
coo 第一个 vector:存储所有的非零元素
coo 第二个 vector:存储非零元素的 column index
coo 第三个 vector:存储非零元素的 row index
2. compressed sparse row - csr
与 coo 类似,唯一的区别是在第三个 vector
第三个 vector 存储的是 row 的指针,如下图所示,存储某个 row 的第一个元素
3. compressed sparse column - csc
与 csr 同理, 第二个 vector 存储的是 column 的指针
4. block sparse row - bsr
bsr 有四个 vector
第一个 vector 存储非零元素
第二个 vector 存储 column index
第三个 vector 和 第四个 vector 存储的都是 row 指针,但是分别是第一个元素和最后一个元素
pointer begin and pointer end
人工智能
2018-10-10 10:49:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
之前在spark环境中一直用的是portable版本,词条数量不是很够,且有心想把jieba,swcs词典加进来,
其他像ik,ansi-seg等分词词典由于没有词性并没有加进来. 本次修改主要是采用jar包方包将词典目录
data与hanlp.properties合成一个data.jar文件.
1. pom.xml 过滤资源文件的配置

org.apache.maven.plugins
maven-jar-plugin
${maven-jar-plugin.version}


**/*.properties



这里把properties文件从jar包文件中去掉,因而结果文件是没有properties文件的.
可根据需要来确定是否把properties加入jar包中.由于我打算把hanlp.properties与词典目录写在一起
这里是要过滤掉hanlp.properties文件

2. 修改hanlp.properties文件
root=
#将根目录置为空,或者注释掉root
CustomDictionaryPath=data/dictionary/custom/CustomDictionary.txt; scws.txt; jieba.txt; 现代汉语补充词库.txt; 全国地名大全.txt ns; 人名词典.txt; 机构名词典.txt; 上海地名.txt ns;data/dictionary/person/nrf.txt nrf;
#增加更多的配置文件,这里增加了结巴分词,scws分词
#IOAdapter=com.hankcs.hanlp.corpus.io.FileIOAdapter
IOAdapter=com.hankcs.hanlp.corpus.io.JarIOAdapter
#修改IOAdapter,以便使用jar包形式加载词典


3. 修改HanLP.java
if ( root.length() != 0 && !root.endsWith("/")) root += "/";
当root的长度为0时,不用在root字符串后面添加'/'

4. 增加处理词典jar包的代码文件: JarIOAdapter.java

package com.hankcs.hanlp.corpus.io;

import java.io.*;

/**
* 基于普通文件系统的IO适配器
*
* @author hankcs
*/
public class JarIOAdapter implements IIOAdapter
{
@Override
public InputStream open(String path) throws FileNotFoundException
{
/*
采用第一行的方式加载资料会在分布式环境报错
改用第二行的方式
*/
//return ClassLoader.getSystemClassLoader().getResourceAsStream(path);
return JarIOAdapter.class.getClassLoader().getResourceAsStream(path);
}

@Override
public OutputStream create(String path) throws FileNotFoundException
{
return new FileOutputStream(path);
}
}

在跑DemoStopWord时,发现
java -cp .:hanlp-1.3.2.jar:test.jar com.hankcs.demo.DemoStopWord
报错,原因是接口不统一导致. 修改
DMAG.java如下:
public MDAG(File dataFile) throws IOException
{
BufferedReader dataFileBufferedReader = new BufferedReader(new InputStreamReader(IOAdapter == null ?
new FileInputStream(dataFile) :
//IOAdapter.open(dataFile.getAbsolutePath())
IOAdapter.open(dataFile.getPath())
, "UTF-8"));
即可.


5. 如何将词典与配置文件打成一个jar包
最好是把txt格式的文件做成bin或dat格式的文件,然后做成jar包,否则打包运行后无法再写成bin或dat格式文件.
简单的办法是跑一下示例,即可生成相应的bin或dat格式文件.
java -cp .:hanlp-1.3.2.jar:test.jar com.hankcs.demo.DemoAtFirstSight
java -cp .:hanlp-1.3.2.jar:test.jar com.hankcs.demo.DemoChineseNameRecognition
java -cp .:hanlp-1.3.2.jar:test.jar com.hankcs.demo.DemoJapaneseNameRecognition
java -cp .:hanlp-1.3.2.jar:test.jar com.hankcs.demo.DemoPinyin
java -cp .:hanlp-1.3.2.jar:test.jar com.hankcs.demo.DemoPlaceRecognition
java -cp .:hanlp-1.3.2.jar:test.jar com.hankcs.demo.DemoOrganizationRecognition
java -cp .:hanlp-1.3.2.jar:test.jar com.hankcs.demo.DemoTokenizerConfig #命名实体识别,包括上面的人名,地名等
java -cp .:hanlp-1.3.2.jar:test.jar com.hankcs.demo.DemoTraditionalChinese2SimplifiedChinese
java -cp .:hanlp-1.3.2.jar:test.jar com.hankcs.demo.DemoStopWord
或者用以下shell脚本完成
:>a;while read cl; do echo $cl; echo "=========="$cl"=======" >>a;java -cp .:test.jar:hanlp-1.3.2.jar $cl 1>> a 2>&1;done < <(jar tvf test.jar | awk '$(NF)~"Demo"{print $(NF)}' | sed 's/.class$//;s/\//./g')

我们把data目录与hanlp.properties文件放在一个目录,比如xxx目录
cd xxx
jar cvf data.jar .
即可生成data.jar包

6. 如何运行
[dxp@Flyme-SearchTag-32-220 makeNewDict]$ ls
data.jar hanlp-1.3.2.jar README.md test test.jar
[dxp@Flyme-SearchTag-32-220 makeNewDict]$ java -cp data.jar:hanlp-1.3.2.jar:test.jar com.hankcs.demo.DemoAtFirstSight

7. 在spark中应用
IDE如(intellij idea)中maven项目
引入以下依赖:

com.hankcs
hanlp
1.3.2
system
${LocalPath}/hanlp-1.3.2.jar


spark-submit提交任务时增加
--jar hanlp-1.3.2.jar,data.jar
转载自 cicido的个人空间
人工智能
2018-10-10 09:18:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
【一】代码下载
https://github.com/tensorflow/tensorflow/releases/
PS:本次源码分析采用1.11版本
【二】Session简介
在TensorFlow中,session是沟通tf的桥梁,模型的训练、推理,都需要通过session,session持有graph的引用。tf支持单机与分布式,因此session也分为单机版与分布式版。
【三】session类图
Session ::tensorflow\core\public\session.h
DirectSession ::tensorflow\core\common_runtime\direct_session.h
GrpcSession ::tensorflow\core\distributed_runtime\rpc\grpc_session.h
class DirectSession : public Session --->单机版session
class GrpcSession : public Session ---> 分布式版session
【四】session创建
tf对外提供了一套C API,负责给client语言使用。在tensorflow\c\c_api.h中,session的创建流程我们从这里开始。
TF_NewSession
NewSession (tensorflow\core\public\session.h)
1. NewSession实现 tensorflow\core\common_runtime\session.cc
Status NewSession(const SessionOptions& options, Session** out_session) {
SessionFactory* factory;
Status s = SessionFactory::GetFactory(options, &factory); // 通过SessionOptions来选择不同的factory
s = factory->NewSession(options, out_session); // 调用对应的factory来创建对应的session,也就是创建DirectSession 还GrpcSession
}
2. 关于SessionOptions
这里看一下该定义,省略掉其他成员,仅仅看看target,注释中说,如果target为空,则创建DirectSession,如果target以 'grpc’开头,则创建GrpcSession
/// Configuration information for a Session.
struct SessionOptions {
/// \brief The TensorFlow runtime to connect to.
///
/// If 'target' is empty or unspecified, the local TensorFlow runtime
/// implementation will be used. Otherwise, the TensorFlow engine
/// defined by 'target' will be used to perform all computations.
///
/// "target" can be either a single entry or a comma separated list
/// of entries. Each entry is a resolvable address of the
/// following format:
/// local
/// ip:port
/// host:port
/// ... other system-specific formats to identify tasks and jobs ...
///
/// NOTE: at the moment 'local' maps to an in-process service-based
/// runtime.
///
/// Upon creation, a single session affines itself to one of the
/// remote processes, with possible load balancing choices when the
/// "target" resolves to a list of possible processes.
///
/// If the session disconnects from the remote process during its
/// lifetime, session calls may fail immediately.
string target;
};
3. SessionFactory::GetFactory
这里的关键在于:session_factory.second->AcceptsOptions(options)
依据选项来,这里实际上是调用对应的factory方法
DirectSessionFactory
bool AcceptsOptions(const SessionOptions& options) override {
return options.target.empty(); // target为空则为true
}
GrpcSessionFactory
bool AcceptsOptions(const SessionOptions& options) override {
return str_util::StartsWith(options.target, kSchemePrefix); // const char* const kSchemePrefix = "grpc://"; target为grpc开头
}

class GrpcSessionFactory : public SessionFactory
class DirectSessionFactory : public SessionFactory
【五】总结
session的创建通过option(可以理解为配置)来选择性创建,而session的真正创建由sessionfactory来完成,这里采用了工厂设计模式,将各个factory通过注册的方式注册到sessionfactory中,这样在系统启动后,这些factory就可以用了,新增一个factory也很容易,且不影响上层代码。



人工智能
2018-10-09 20:42:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
一、前言
计算机视觉长久以来没有大的突破,卷积神经网络的出现,给这一领域带来了突破,本篇博客,将通过具体的实例来看看卷积神经网络在图像识别上的应用。
导读
1、问题描述
2、解决问题的思路
3、用DL4J进行实现
二、问题
有如下一组验证码的图片,图片大小为60*160,验证码由5个数字组成,数字的范围为0到9,并且每个验证码图片上都加上了干扰背景,图片的文件名,表示验证码上的数字,样本图片如下:
穷举每张图片的可能性几乎不可能,所以传统的程序思路不可能解这个问题,那么必须让计算机通过自我学习,获取识别验证码的能力。先让计算机看大量的验证码的图片,并告诉计算机这些图片的结果,让计算机自我学习,慢慢地计算机就学会了识别验证码。
三、解决思路
1、特征
每个数字的形状各异,各自特征明显,这里的特征实际上指的是线条的走向、弯曲程度等等形状上的不同表征,那么对于侦测图形上的形状,卷积神经网络加上Relu和Max采样,可以很精确的做到这一点,本质原因在于,把卷积核拉直了看,本质上所做的事情就算向量的点积运算,求一个向量在另一个向量上的投影。对于卷积神经网络的原理可以看看 《有趣的卷积神经网络》
2、网络结构设计
对于每张图片而言,有5个数字作为输出结果,那么得设计一个有5个output的深度神经网络,首先用多个卷积核+Max采样层的结构来抽取明显特征,最后获得的特征经过两个全连接层逼近,这里加全连接层有两个目的,第一:经过sigmoid函数把值压缩到0到1之间,便于softmax计算,第二,加上全连接层可以更加抽象特征,让函数的逼近更加容易。最终的网络结构如下:
3、张量表示
对于Label的表示用one-hot来表示,这样可以很好的配合softmax,下图展示了从0到9的数字表示,沿着行的方向,由上而下,分别表示0到9

对于图片上的像素点,值域在0到255之间,图片如果是彩色,那么实际上会有三个通道,这里都是黑白色,所以,只有一个通道,取图片上真实像素点的值,除以255进行归一化即可。
四、代码实现
1、网络结构 public static ComputationGraph createModel() { ComputationGraphConfiguration config = new NeuralNetConfiguration.Builder() .seed(seed) .gradientNormalization(GradientNormalization.RenormalizeL2PerLayer) .l2(1e-3) .updater(new Adam(1e-3)) .weightInit( WeightInit.XAVIER_UNIFORM) .graphBuilder() .addInputs("trainFeatures") .setInputTypes(InputType.convolutional(60, 160, 1)) .setOutputs("out1", "out2", "out3", "out4", "out5", "out6") .addLayer("cnn1", new ConvolutionLayer.Builder(new int[]{5, 5}, new int[]{1, 1}, new int[]{0, 0}) .nIn(1).nOut(48).activation( Activation.RELU).build(), "trainFeatures") .addLayer("maxpool1", new SubsamplingLayer.Builder(PoolingType.MAX, new int[]{2,2}, new int[]{2, 2}, new int[]{0, 0}) .build(), "cnn1") .addLayer("cnn2", new ConvolutionLayer.Builder(new int[]{5, 5}, new int[]{1, 1}, new int[]{0, 0}) .nOut(64).activation( Activation.RELU).build(), "maxpool1") .addLayer("maxpool2", new SubsamplingLayer.Builder(PoolingType.MAX, new int[]{2,1}, new int[]{2, 1}, new int[]{0, 0}) .build(), "cnn2") .addLayer("cnn3", new ConvolutionLayer.Builder(new int[]{3, 3}, new int[]{1, 1}, new int[]{0, 0}) .nOut(128).activation( Activation.RELU).build(), "maxpool2") .addLayer("maxpool3", new SubsamplingLayer.Builder(PoolingType.MAX, new int[]{2,2}, new int[]{2, 2}, new int[]{0, 0}) .build(), "cnn3") .addLayer("cnn4", new ConvolutionLayer.Builder(new int[]{4, 4}, new int[]{1, 1}, new int[]{0, 0}) .nOut(256).activation( Activation.RELU).build(), "maxpool3") .addLayer("maxpool4", new SubsamplingLayer.Builder(PoolingType.MAX, new int[]{2,2}, new int[]{2, 2}, new int[]{0, 0}) .build(), "cnn4") .addLayer("ffn0", new DenseLayer.Builder().nOut(3072) .build(), "maxpool4") .addLayer("ffn1", new DenseLayer.Builder().nOut(3072) .build(), "ffn0") .addLayer("out1", new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1") .addLayer("out2", new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1") .addLayer("out3", new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1") .addLayer("out4", new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1") .addLayer("out5", new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1") .addLayer("out6", new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) .nOut(10).activation(Activation.SOFTMAX).build(), "ffn1") .pretrain(false).backprop(true) .build(); ComputationGraph model = new ComputationGraph(config); model.init(); return model; }
2、训练集构建 public MultiDataSet convertDataSet(int num) throws Exception { int batchNumCount = 0; INDArray[] featuresMask = null; INDArray[] labelMask = null; List multiDataSets = new ArrayList<>(); while (batchNumCount != num && fileIterator.hasNext()) { File image = fileIterator.next(); String imageName = image.getName().substring(0,image.getName().lastIndexOf('.')); String[] imageNames = imageName.split(""); INDArray feature = asMatrix(image); INDArray[] features = new INDArray[]{feature}; INDArray[] labels = new INDArray[6]; Nd4j.getAffinityManager().ensureLocation(feature, AffinityManager.Location.DEVICE); if (imageName.length() < 6) { imageName = imageName + "0"; imageNames = imageName.split(""); } for (int i = 0; i < imageNames.length; i ++) { int digit = Integer.parseInt(imageNames[i]); labels[i] = Nd4j.zeros(1, 10).putScalar(new int[]{0, digit}, 1); } feature = feature.muli(1.0/255.0); multiDataSets.add(new MultiDataSet(features, labels, featuresMask, labelMask)); batchNumCount ++; } MultiDataSet result = MultiDataSet.merge(multiDataSets); return result; }
五、后记
用deeplearning4j构建一个深度神经网络,几乎没有多余的代码,非常优雅就可以解一个复杂的图像识别问题,对于上述代码有几点说明:
1、对于DenseLayer层,这里没有设置网络输入的size,实际上在dl4j内部已经做了这个set操作
2、对于梯度更新优化,这里选用Adam,Adam融合了动量和自适应learningRate两方面的因素,通常会有更好的效果
3、损失函数用的类Log函数,和交叉熵有相同的效果
4、模型训练好可以使用 ModelSerializer.writeModel(model, modelPath, true)来保存网络结构,就可以用于图像识别了
完整的代码,可以查看deeplearning4j的example

---------------------------------------------------------------------------------------------------------
快乐源于分享。
此博客乃作者原创, 转载请注明出处
人工智能
2018-10-09 20:02:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
最近在看《机器学习实战》这本书,因为自己本身很想深入的了解机器学习算法,加之想学python,就在朋友的推荐之下选择了这本书进行学习。
一 . K-近邻算法(KNN)概述
最简单最初级的分类器是将全部的训练数据所对应的类别都记录下来,当测试对象的属性和某个训练对象的属性完全匹配时,便可以对其进行分类。但是怎么可能所有测试对象都会找到与之完全匹配的训练对象呢,其次就是存在一个测试对象同时与多个训练对象匹配,导致一个训练对象被分到了多个类的问题,基于这些问题呢,就产生了KNN。
KNN是通过测量不同特征值之间的距离进行分类。它的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别,其中K通常是不大于20的整数。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。
下面通过一个简单的例子说明一下:如下图,绿色圆要被决定赋予哪个类,是红色三角形还是蓝色四方形?如果K=3,由于红色三角形所占比例为2/3,绿色圆将被赋予红色三角形那个类,如果K=5,由于蓝色四方形比例为3/5,因此绿色圆被赋予蓝色四方形类。

由此也说明了KNN算法的结果很大程度取决于K的选择。
在KNN中,通过计算对象间距离来作为各个对象之间的非相似性指标,避免了对象之间的匹配问题,在这里距离一般使用欧氏距离或曼哈顿距离:

同时,KNN通过依据k个对象中占优的类别进行决策,而不是单一的对象类别决策。这两点就是KNN算法的优势。
接下来对KNN算法的思想总结一下:就是在训练集中数据和标签已知的情况下,输入测试数据,将测试数据的特征与训练集中对应的特征进行相互比较,找到训练集中与之最为相似的前K个数据,则该测试数据对应的类别就是K个数据中出现次数最多的那个分类,其算法的描述为:
1)计算测试数据与各个训练数据之间的距离;
2)按照距离的递增关系进行排序;
3)选取距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。
二 .python实现
首先呢,需要说明的是我用的是python3.4.3,里面有一些用法与2.7还是有些出入。
建立一个KNN.py文件对算法的可行性进行验证,如下: #coding:utf-8 from numpy import * import operator ##给出训练数据以及对应的类别 def createDataSet(): group = array([[1.0,2.0],[1.2,0.1],[0.1,1.4],[0.3,3.5]]) labels = ['A','A','B','B'] return group,labels ###通过KNN进行分类 def classify(input,dataSe t,label,k): dataSize = dataSet.shape[0] ####计算欧式距离 diff = tile(input,(dataSize,1)) - dataSet sqdiff = diff ** 2 squareDist = sum(sqdiff,axis = 1)###行向量分别相加,从而得到新的一个行向量 dist = squareDist ** 0.5 ##对距离进行排序 sortedDistIndex = argsort(dist)##argsort()根据元素的值从大到小对元素进行排序,返回下标 classCount={} for i in range(k): voteLabel = label[sortedDistIndex[i]] ###对选取的K个样本所属的类别个数进行统计 classCount[voteLabel] = classCount.get(voteLabel,0) + 1 ###选取出现的类别次数最多的类别 maxCount = 0 for key,value in classCount.items(): if value > maxCount: maxCount = value classes = key return classes
接下来,在命令行窗口输入如下代码: #-*-coding:utf-8 -*- import sys sys.path.append("...文件路径...") import KNN from numpy import * dataSet,labels = KNN.createDataSet() input = array([1.1,0.3]) K = 3 output = KNN.classify(input,dataSet,labels,K) print("测试数据为:",input,"分类结果为:",output)
回车之后的结果为:
测试数据为: [ 1.1 0.3] 分类为: A
答案符合我们的预期,要证明算法的准确性,势必还需要通过处理复杂问题进行验证,之后另行说明。

这是第一次用python编的一个小程序,势必会遇到各种问题,在此次编程调试过程中遇到了如下问题: 导入.py文件路径有问题,因此需要在最开始加如下代码: import sys sys.path.append("文件路径")
; 在python提示代码存在问题时,一定要及时改正,改正之后保存之后再执行命令行,这一点跟MATLAB是不一样的,所以在python中最好是敲代码的同时在命令行中一段一段的验证; 在调用文件时函数名一定要写正确,否则会出现:'module' object has no attribute 'creatDataSet'; 'int' object has no attribute 'kclassify',这个问题出现的原因是之前我讲文件保存名为k.py,在执行output = K.classify(input,dataSet,labels,K)这一句就会出错。根据函数式编程的思想,每个函数都可以看为是一个变量而将K赋值后,调用k.py时就会出现问题。
三 MATLAB实现
之前一直在用MATLAB做聚类算法的一些优化,其次就是数模的一些常用算法,对于别的算法,还真是没有上手编过,基础还在,思想还在,当然要动手编一下,也是不希望在学python的同时对MATLAB逐渐陌生吧,走走停停,停很重要。 首先,建立KNN.m文件,如下: %% KNN clear all clc %% data trainData = [1.0,2.0;1.2,0.1;0.1,1.4;0.3,3.5]; trainClass = [1,1,2,2]; testData = [0.5,2.3]; k = 3; %% distance row = size(trainData,1); col = size(trainData,2); test = repmat(testData,row,1); dis = zeros(1,row); for i = 1:row diff = 0; for j = 1:col diff = diff + (test(i,j) - trainData(i,j)).^2; end dis(1,i) = diff.^0.5; end %% sort jointDis = [dis;trainClass]; sortDis= sortrows(jointDis'); sortDisClass = sortDis'; %% find class = sort(2:1:k); member = unique(class); num = size(member); max = 0; for i = 1:num count = find(class == member(i)); if count > max max = count; label = member(i); end end disp('最终的分类结果为:'); fprintf('%d\n',label)
运行之后的结果是,最终的分类结果为:2。和预期结果一样。
四 实战
之前,对KNN进行了一个简单的验证,今天我们使用KNN改进约会网站的效果,个人理解,这个问题也可以转化为其它的比如各个网站迎合客户的喜好所作出的推荐之类的,当然,今天的这个例子功能也实在有限。
在这里根据一个人收集的约会数据,根据主要的样本特征以及得到的分类,对一些未知类别的数据进行分类,大致就是这样。
我使用的是python 3.4.3,首先建立一个文件,例如date.py,具体的代码如下: #coding:utf-8 from numpy import * import operator from collections import Counter import matplotlib import matplotlib.pyplot as plt ###导入特征数据 def file2matrix(filename): fr = open(filename) contain = fr.readlines()###读取文件的所有内容 count = len(contain) returnMat = zeros((count,3)) classLabelVector = [] index = 0 for line in contain: line = line.strip() ###截取所有的回车字符 listFromLine = line.split('\t') returnMat[index,:] = listFromLine[0:3]###选取前三个元素,存储在特征矩阵中 classLabelVector.append(listFromLine[-1])###将列表的最后一列存储到向量classLabelVector中 index += 1 ##将列表的最后一列由字符串转化为数字,便于以后的计算 dictClassLabel = Counter(classLabelVector) classLabel = [] kind = list(dictClassLabel) for item in classLabelVector: if item == kind[0]: item = 1 elif item == kind[1]: item = 2 else: item = 3 classLabel.append(item) return returnMat,classLabel#####将文本中的数据导入到列表 ##绘图(可以直观的表示出各特征对分类结果的影响程度) datingDataMat,datingLabels = file2matrix('D:\python\Mechine learing in Action\KNN\datingTestSet.txt') fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(datingDataMat[:,0],datingDataMat[:,1],15.0*array(datingLabels),15.0*array(datingLabels)) plt.show() ## 归一化数据,保证特征等权重 def autoNorm(dataSet): minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals normDataSet = zeros(shape(dataSet))##建立与dataSet结构一样的矩阵 m = dataSet.shape[0] for i in range(1,m): normDataSet[i,:] = (dataSet[i,:] - minVals) / ranges return normDataSet,ranges,minVals ##KNN算法 def classify(input,dataSet,label,k): dataSize = dataSet.shape[0] ####计算欧式距离 diff = tile(input,(dataSize,1)) - dataSet sqdiff = diff ** 2 squareDist = sum(sqdiff,axis = 1)###行向量分别相加,从而得到新的一个行向量 dist = squareDist ** 0.5 ##对距离进行排序 sortedDistIndex = argsort(dist)##argsort()根据元素的值从大到小对元素进行排序,返回下标 classCount={} for i in range(k): voteLabel = label[sortedDistIndex[i]] ###对选取的K个样本所属的类别个数进行统计 classCount[voteLabel] = classCount.get(voteLabel,0) + 1 ###选取出现的类别次数最多的类别 maxCount = 0 for key,value in classCount.items(): if value > maxCount: maxCount = value classes = key return classes ##测试(选取10%测试) def datingTest(): rate = 0.10 datingDataMat,datingLabels = file2matrix('D:\python\Mechine learing in Action\KNN\datingTestSet.txt') normMat,ranges,minVals = autoNorm(datingDataMat) m = normMat.shape[0] testNum = int(m * rate) errorCount = 0.0 for i in range(1,testNum): classifyResult = classify(normMat[i,:],normMat[testNum:m,:],datingLabels[testNum:m],3) print("分类后的结果为:,", classifyResult) print("原结果为:",datingLabels[i]) if(classifyResult != datingLabels[i]): errorCount += 1.0 print("误分率为:",(errorCount/float(testNum))) ###预测函数 def classifyPerson(): resultList = ['一点也不喜欢','有一丢丢喜欢','灰常喜欢'] percentTats = float(input("玩视频所占的时间比?")) miles = float(input("每年获得的飞行常客里程数?")) iceCream = float(input("每周所消费的冰淇淋公升数?")) datingDataMat,datingLabels = file2matrix('D:\python\Mechine learing in Action\KNN\datingTestSet2.txt') normMat,ranges,minVals = autoNorm(datingDataMat) inArr = array([miles,percentTats,iceCream]) classifierResult = classify((inArr-minVals)/ranges,normMat,datingLabels,3) print("你对这个人的喜欢程度:",resultList[classifierResult - 1])
新建test.py文件了解程序的运行结果,代码: #coding:utf-8 from numpy import * import operator from collections import Counter import matplotlib import matplotlib.pyplot as plt import sys sys.path.append("D:\python\Mechine learing in Action\KNN") import date date.classifyPerson()
运行结果如下图:

以上,是对本次算法的整理和总结。
人工智能
2018-10-09 14:59:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前不久,主题为“人工智能赋能新时代”的2018年世界人工智能大会在上海举行,吸引了众多世界顶尖科技企业参与。国家主席习近平在贺信中指出,“新一代人工智能正在全球范围内蓬勃兴起,为经济社会发展注入了新动能,正在深刻改变人们的生产生活方式”。
乘着互联网、大数据、人工智能等技术发展的东风,近年来数字经济在我国得到了很好的成长。根据《中国“互联网+”指数报告(2018)》的数据,2017年全国数字经济体量为26.70万亿元,较上年同期的22.77万亿元增长17.24%,数字经济占国内生产总值(GDP)的比重也由30.61%提升至32.28%。
可以看到,在大数据、人工智能等技术的支持下,互联网经济正呈现蓬勃发展之势,不仅成为拉动我国经济发展的新引擎,更为实体经济的发展提供了新机遇。
改革开放四十年来,我国以制造业为主体的实体经济实现了跨越式发展。在我国成为全球制造业第一大国的同时,产业技术能力也正在逐渐追上世界先进水平。经济新常态需要新动能的出现,这在党的十九大报告中即有指出,“推动互联网、大数据、人工智能和实体经济的深度融合,在中高端消费、创新引领、绿色低碳、共享经济、现代供应链、人力资本服务等领域培育新增长点,形成新动能”。
这正是解决当前实体经济发展所遇问题的新方案。在新一轮产业革命的背景下,融合发展是供给侧结构性改革的必由之路,也有助于化解当前人民日益增长的美好生活需要和不平衡不充分的发展之间的矛盾。
作为新一代信息技术的典型,互联网、大数据、人工智能在全世界范围内都受到了高度重视,尤其是人工智能技术,被认为是下一个超级风口,其中包含的机会不言而喻。传统行业若能与之深度融合,必然会为实体经济提供有力驱动,也将为高质量发展打下坚实的基础。
事实上,当前部分行业已经成功引入了相关新技术。以零售业为例,在线上线下“共振”的新模式出现之前,传统零售业和电商一直处于比较对立的位置,一边是电商的快速发展扩张,一边是传统零售的“半死不活”。而近年来新出现的“无界零售”“新零售”模式,则是利用大数据、人工智能技术,旨在推动线上线下一体化。新模式出现之后,不仅线下实体呈现出起死回生的趋势,线上消费购物体验不足的弊端也得到了有效缓解,“互联网+”通过赋能的形式造就了双赢的局面。
人工智能
2018-10-09 11:11:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
美国和中国已经是毋庸置疑的AI超级体了,那么欧盟有可能成为下一个吗?
德国总理安格拉·默克尔以冷酷无情著称,但需要的时候她也可以很幽默。
在最近一次由科技杂志《Ada》举办的季度会议上,有人问她是否认为机器人应当拥有权利,她回答说:“你的意思是用电的权利?还是定期维护的权利?”
导致这次访谈如此引人入胜的还有另一个原因。默克尔表示,她对人工智能(AI)及其地缘政治很感兴趣。“在美国,对个人数据的控制很大程度上是私人公司在做的。而在中国正好相反,政府控制一切。”她同时表明,欧洲会介于这两者之间。
这种思想是欧洲更广泛的现实化的一部分,即AI在未来应当成为像电力、蒸汽机等基础科技同样重要的技术。一些国家如芬兰、法国已经制定了国家级人工智能战略,而德国正在制定中,预计将在今年末完成。届时,欧洲将会把这些努力统合到统一的人工智能计划中。预料之中的是,这个计划完全由欧盟成员组成,涉及了十几个委员会和其他实体。
但默克尔女士提出的问题对于欧洲就像英国脱欧和移民问题一样重要:欧洲能否在美国和中国的AI超级力量之间获得一席之地?
欧洲能否成为下一个AI超级力量?
人工智能
2018-10-09 11:08:03
「深度学习福利」大神带你进阶工程师,立即查看>>>
微软语音识别SDK总结
代码 CComPtr m_pSREngine;// 语音识别引擎(recognition)的接口。 CComPtr m_pSRContext;// 识别引擎上下文(context)的接口。 CComPtr m_pSRGrammar;// 识别文法(grammar)的接口。 CComPtr m_pInputStream;// 流()的接口。 CComPtr m_pToken;// 语音特征的(token)接口。 CComPtr m_pAudio;// 音频(Audio)的接口。(用来保存原来默认的输入流) ULONGLONG ullGrammerID ; CoInitialize(NULL); m_pSREngine.CoCreateInstance ( CLSID_SpInprocRecognizer ); m_pSREngine->CreateRecoContext ( &m_pSRContext );//建立上下文 //这里是设置事件 HWND hwnd = GetSafeHwnd(); hr = m_pSRContext->SetNotifyWindowMessage(hwnd,WM_RECORD,0,0); hr=m_pSRContext->SetInterest(SPFEI(SPEI_RECOGNITION),SPFEI(SPEI_RECOGNITION)); //这里是设置默认的音频输入 hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOIN, &m_pAudio); m_pSREngine->SetInput(m_pAudio,true); //这里是加载默认的语法规则 ullGrammerID = 1000; hr=m_pSRContext->CreateGrammar(ullGrammerID,&m_pSRGrammar); WCHAR wszXMLFile[20]=L""; MultiByteToWideChar(CP_ACP, 0,(LPCSTR)"cmd.xml" , -1, wszXMLFile, 256); //这里修改XML的目录 hr=m_pSRGrammar->LoadCmdFromFile(wszXMLFile,SPLO_DYNAMIC); //开启语音识别 m_pSRGrammar->SetRuleState( NULL,NULL,SPRS_ACTIVE ); hr=m_pSREngine->SetRecoState(SPRST_ACTIVE);
简单介绍
ISpRecognizer
There are two implementations of the ISpRecognizer and ISpRecoContext in SAPI. One is for recognition "in-process" (InProc), where the SR engine is created in the same process as the application. Only this application can connect to this recognizer. The other implementation is the "shared-recognizer," where the SR engine is created in a separate process. There will only be one shared engine running on a system, and all applications using the shared engine connect to the same recognizer. This allows several speech applications to work simultaneously, and allows the user to speak to any application, as recognition is done from the grammars of all applications. For desktop-based speech applications it is recommended to use the shared recognizer because of the way it allows multiple SAPI applications to work at once. For other types of application, such as recognizing from wave files or a telephony server application where multiple SR engines will be required, the InProc recognizer should be used. When to Use
Call methods of the ISpRecognizer interface to configure or retrieve the attributes of the SR engine. How Created
There are two objects that implement this interface. These are created by applications by creating a COM object with either of the following CLSIDs:
SpInprocRecognizer (CLSID_SpInprocRecognizer)
SpSharedRecognizer (CLSID_SpSharedRecognizer)
Alternatively, the shared recognizer can be created by creating a SpSharedRecoContext (CLSID_SpSharedRecoContext), and then calling ISpRecoContext::GetRecognizer on this object to get a reference to the SpSharedRecognizer object.
Methods in Vtable Order
ISpRecognizer Methods Description SetRecognizer Specifies the SR engine to be used.
GetRecognizer Retrieves which SR engine is currently being used.
SetInput Specifies which input stream the SR engine should use.
GetInputObjectToken Retrieves the input token object for the stream.
GetInputStream Retrieves the input stream.
CreateRecoContext Creates a recognition context for this instance of an SR engine.
GetRecoProfile Retrieves the current recognition profile token.
SetRecoProfile Sets the recognition profile to be used by the recognizer.
IsSharedInstance Determines if the recognizer is the shared or InProc implementation.
GetRecoState Retrieves the state of the recognition engine.
SetRecoState Sets the state of the recognition engine.
GetStatus Retrieves current status information for the engine.
GetFormat Retrieves the format of the current audio input.
IsUISupported Checks if the SR engine supports a particular user interface component.
DisplayUI Displays a user interface component.
EmulateRecognition Emulates a recognition from a text phrase rather than from spoken audio.
context
ISpRecognizer::CreateRecoContext creates a recognition context for this instance of an SR engine. The recognition context is used to load recognition grammars, start and stop recognition, and receive events and recognition results.
SetNotifyWindowMessage
ISpNotifySource::SetNotifyWindowMessage
ISpNotifySource::SetNotifyWindowMessage sets up the instance to send window messages to a specified window. HRESULT SetNotifyWindowMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
input
SpCreateDefaultObjectFromCategoryId
SpCreateDefaultObjectFromCategoryId creates the object instance from the default object token of a specified category.
Found in: sphelper.h SpCreateDefaultObjectFromCategoryId( const WCHAR *pszCategoryId, T **ppObject, IUnknown *pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL );
人工智能
2018-10-09 00:29:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
解决方案
// QT,把#define Q_ENABLE_OPENGL_FUNCTIONS_DEBUG打开,这样所有调用OpenGL接口时就会有提醒
new THREE.SkinnedMesh(geometry,new THREE.MeshFaceMaterial(materials), false )
THREE.SkinnedMesh原始官方API为两参数,qml改造后为三参数,第三个参数默认为true,当模型没有使用节点渲染方式将会产生严重问题。故第三个参数设置为false
问题原因分析 Starting C:\Qt\Qt5.6.2\Examples\Qt-5.6\canvas3d\build-threejs-Desktop_Qt_5_6_2_MinGW_32bit-Debug\cellphone\debug\cellphone.exe... QML debugging is enabled. Only use this in a safe environment. Debug: THREE.Canvas3DRenderer 71 (qrc:/three.js:29042, ) Warning: QOpenGLShader::compile(Vertex): ERROR: 3:217: '' : array size must be a positive integer (opengl\qopenglshaderprogram.cpp:328, bool QOpenGLShaderPrivate::compile(QOpenGLShader*)) Warning: *** Problematic Vertex shader source code *** (opengl\qopenglshaderprogram.cpp:334, bool QOpenGLShaderPrivate::compile(QOpenGLShader*)) Warning: #version 120 #define lowp #define mediump #define highp #line 2 #define precision precision highp float; precision highp int; #define VERTEX_TEXTURES #define GAMMA_FACTOR 2 #define MAX_DIR_LIGHTS 1 #define MAX_POINT_LIGHTS 0 #define MAX_SPOT_LIGHTS 0 #define MAX_HEMI_LIGHTS 0 #define MAX_SHADOWS 0 #define MAX_BONES 0 #define USE_COLOR #define USE_SKINNING uniform mat4 modelMatrix; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; uniform mat4 viewMatrix; uniform mat3 normalMatrix; uniform vec3 cameraPosition; attribute vec3 position; attribute vec3 normal; attribute vec2 uv; attribute vec2 uv2; #ifdef USE_COLOR attribute vec3 color; #endif #ifdef USE_MORPHTARGETS attribute vec3 morphTarget0; attribute vec3 morphTarget1; attribute vec3 morphTarget2; attribute vec3 morphTarget3; #ifdef USE_MORPHNORMALS attribute vec3 morphNormal0; attribute vec3 morphNormal1; attribute vec3 morphNormal2; attribute vec3 morphNormal3; #else attribute vec3 morphTarget4; attribute vec3 morphTarget5; attribute vec3 morphTarget6; attribute vec3 morphTarget7; #endif #endif #ifdef USE_SKINNING attribute vec4 skinIndex; attribute vec4 skinWeight; #endif #define PHONG varying vec3 vViewPosition; #ifndef FLAT_SHADED varying vec3 vNormal; #endif #define PI 3.14159 #define PI2 6.28318 #define RECIPROCAL_PI2 0.15915494 #define LOG2 1.442695 #define EPSILON 1e-6 float square( in float a ) { return a*a; } vec2 square( in vec2 a ) { return vec2( a.x*a.x, a.y*a.y ); } vec3 square( in vec3 a ) { return vec3( a.x*a.x, a.y*a.y, a.z*a.z ); } vec4 square( in vec4 a ) { return vec4( a.x*a.x, a.y*a.y, a.z*a.z, a.w*a.w ); } float saturate( in float a ) { return clamp( a, 0.0, 1.0 ); } vec2 saturate( in vec2 a ) { return clamp( a, 0.0, 1.0 ); } vec3 saturate( in vec3 a ) { return clamp( a, 0.0, 1.0 ); } vec4 saturate( in vec4 a ) { return clamp( a, 0.0, 1.0 ); } float average( in float a ) { return a; } float average( in vec2 a ) { return ( a.x + a.y) * 0.5; } float average( in vec3 a ) { return ( a.x + a.y + a.z) / 3.0; } float average( in vec4 a ) { return ( a.x + a.y + a.z + a.w) * 0.25; } float whiteCompliment( in float a ) { return saturate( 1.0 - a ); } vec2 whiteCompliment( in vec2 a ) { return saturate( vec2(1.0) - a ); } vec3 whiteCompliment( in vec3 a ) { return saturate( vec3(1.0) - a ); } vec4 whiteCompliment( in vec4 a ) { return saturate( vec4(1.0) - a ); } vec3 transformDirection( in vec3 normal, in mat4 matrix ) { return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz ); } // http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations vec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) { return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz ); } vec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal) { float distance = dot( planeNormal, point-pointOnPlane ); return point - distance * planeNormal; } float sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) { return sign( dot( point - pointOnPlane, planeNormal ) ); } vec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) { return pointOnLine + lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ); } float calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) { if ( decayExponent > 0.0 ) { return pow( saturate( 1.0 - lightDistance / cutoffDistance ), decayExponent ); } return 1.0; } vec3 inputToLinear( in vec3 a ) { #ifdef GAMMA_INPUT return pow( a, vec3( float( GAMMA_FACTOR ) ) ); #else return a; #endif } vec3 linearToOutput( in vec3 a ) { #ifdef GAMMA_OUTPUT return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) ); #else return a; #endif } #if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) varying vec2 vUv; uniform vec4 offsetRepeat; #endif #ifdef USE_LIGHTMAP varying vec2 vUv2; #endif #if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG ) varying vec3 vReflect; uniform float refractionRatio; #endif #if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP ) varying vec3 vWorldPosition; #endif #ifdef USE_COLOR varying vec3 vColor; #endif #ifdef USE_MORPHTARGETS #ifndef USE_MORPHNORMALS uniform float morphTargetInfluences[ 8 ]; #else uniform float morphTargetInfluences[ 4 ]; #endif #endif #ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; #ifdef BONE_TEXTURE uniform sampler2D boneTexture; uniform int boneTextureWidth; uniform int boneTextureHeight; mat4 getBoneMatrix( const in float i ) { float j = i * 4.0; float x = mod( j, float( boneTextureWidth ) ); float y = floor( j / float( boneTextureWidth ) ); float dx = 1.0 / float( boneTextureWidth ); float dy = 1.0 / float( boneTextureHeight ); y = dy * ( y + 0.5 ); vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); mat4 bone = mat4( v1, v2, v3, v4 ); return bone; } #else uniform mat4 boneGlobalMatrices[ MAX_BONES ]; mat4 getBoneMatrix( const in float i ) { mat4 bone = boneGlobalMatrices[ int(i) ]; return bone; } #endif #endif #ifdef USE_SHADOWMAP varying vec4 vShadowCoord[ MAX_SHADOWS ]; uniform mat4 shadowMatrix[ MAX_SHADOWS ]; #endif #ifdef USE_LOGDEPTHBUF #ifdef USE_LOGDEPTHBUF_EXT varying float vFragDepth; #endif uniform float logDepthBufFC; #endif void main() { #if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) vUv = uv * offsetRepeat.zw + offsetRepeat.xy; #endif #ifdef USE_LIGHTMAP vUv2 = uv2; #endif #ifdef USE_COLOR vColor.xyz = inputToLinear( color.xyz ); #endif #ifdef USE_MORPHNORMALS vec3 morphedNormal = vec3( 0.0 ); morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ]; morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ]; morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ]; morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ]; morphedNormal += normal; #endif #ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); #endif #ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; skinMatrix += skinWeight.z * boneMatZ; skinMatrix += skinWeight.w * boneMatW; skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; #ifdef USE_MORPHNORMALS vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 ); #else vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 ); #endif #endif #ifdef USE_SKINNING vec3 objectNormal = skinnedNormal.xyz; #elif defined( USE_MORPHNORMALS ) vec3 objectNormal = morphedNormal; #else vec3 objectNormal = normal; #endif #ifdef FLIP_SIDED objectNormal = -objectNormal; #endif vec3 transformedNormal = normalMatrix * objectNormal; #ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #endif #ifdef USE_MORPHTARGETS vec3 morphed = vec3( 0.0 ); morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ]; morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ]; morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ]; morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ]; #ifndef USE_MORPHNORMALS morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ]; morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ]; morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ]; morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ]; #endif morphed += position; #endif #ifdef USE_SKINNING #ifdef USE_MORPHTARGETS vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 ); #else vec4 skinVertex = bindMatrix * vec4( position, 1.0 ); #endif vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; skinned += boneMatY * skinVertex * skinWeight.y; skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; skinned = bindMatrixInverse * skinned; #endif #ifdef USE_SKINNING vec4 mvPosition = modelViewMatrix * skinned; #elif defined( USE_MORPHTARGETS ) vec4 mvPosition = modelViewMatrix * vec4( morphed, 1.0 ); #else vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); #endif gl_Position = projectionMatrix * mvPosition; #ifdef USE_LOGDEPTHBUF gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC; #ifdef USE_LOGDEPTHBUF_EXT vFragDepth = 1.0 + gl_Position.w; #else gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w; #endif #endif vViewPosition = -mvPosition.xyz; #if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP ) #ifdef USE_SKINNING vec4 worldPosition = modelMatrix * skinned; #elif defined( USE_MORPHTARGETS ) vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 ); #else vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); #endif #endif #if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG ) vec3 worldNormal = transformDirection( objectNormal, modelMatrix ); vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); #ifdef ENVMAP_MODE_REFLECTION vReflect = reflect( cameraToVertex, worldNormal ); #else vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif #if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP ) vWorldPosition = worldPosition.xyz; #endif #ifdef USE_SHADOWMAP for( int i = 0; i < MAX_SHADOWS; i ++ ) { vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition; } #endif } (opengl\qopenglshaderprogram.cpp:335, bool QOpenGLShaderPrivate::compile(QOpenGLShader*)) Warning: *** (opengl\qopenglshaderprogram.cpp:336, bool QOpenGLShaderPrivate::compile(QOpenGLShader*)) Warning: CanvasRenderer::executeCommandQueue: Failed to compile shader QOpenGLShader(0x2f6756f0) (canvasrenderer.cpp:1063, void QtCanvas3D::CanvasRenderer::executeCommandQueue()) Critical: THREE.WebGLShader: Shader couldn't compile. (qrc:/three.js:69, ) Warning: THREE.WebGLShader: gl.getShaderInfoLog() ERROR: 3:217: '' : array size must be a positive integer 1: precision highp float; 2: precision highp int; 3: 4: #define VERTEX_TEXTURES 5: 6: 7: #define GAMMA_FACTOR 2 8: #define MAX_DIR_LIGHTS 1 9: #define MAX_POINT_LIGHTS 0 10: #define MAX_SPOT_LIGHTS 0 11: #define MAX_HEMI_LIGHTS 0 12: #define MAX_SHADOWS 0 13: #define MAX_BONES 0 14: 15: 16: 17: 18: 19: 20: 21: 22: #define USE_COLOR 23: 24: #define USE_SKINNING 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: uniform mat4 modelMatrix; 38: uniform mat4 modelViewMatrix; 39: uniform mat4 projectionMatrix; 40: uniform mat4 viewMatrix; 41: uniform mat3 normalMatrix; 42: uniform vec3 cameraPosition; 43: attribute vec3 position; 44: attribute vec3 normal; 45: attribute vec2 uv; 46: attribute vec2 uv2; 47: #ifdef USE_COLOR 48: attribute vec3 color; 49: #endif 50: #ifdef USE_MORPHTARGETS 51: attribute vec3 morphTarget0; 52: attribute vec3 morphTarget1; 53: attribute vec3 morphTarget2; 54: attribute vec3 morphTarget3; 55: #ifdef USE_MORPHNORMALS 56: attribute vec3 morphNormal0; 57: attribute vec3 morphNormal1; 58: attribute vec3 morphNormal2; 59: attribute vec3 morphNormal3; 60: #else 61: attribute vec3 morphTarget4; 62: attribute vec3 morphTarget5; 63: attribute vec3 morphTarget6; 64: attribute vec3 morphTarget7; 65: #endif 66: #endif 67: #ifdef USE_SKINNING 68: attribute vec4 skinIndex; 69: attribute vec4 skinWeight; 70: #endif 71: #define PHONG 72: varying vec3 vViewPosition; 73: #ifndef FLAT_SHADED 74: varying vec3 vNormal; 75: #endif 76: #define PI 3.14159 77: #define PI2 6.28318 78: #define RECIPROCAL_PI2 0.15915494 79: #define LOG2 1.442695 80: #define EPSILON 1e-6 81: 82: float square( in float a ) { return a*a; } 83: vec2 square( in vec2 a ) { return vec2( a.x*a.x, a.y*a.y ); } 84: vec3 square( in vec3 a ) { return vec3( a.x*a.x, a.y*a.y, a.z*a.z ); } 85: vec4 square( in vec4 a ) { return vec4( a.x*a.x, a.y*a.y, a.z*a.z, a.w*a.w ); } 86: float saturate( in float a ) { return clamp( a, 0.0, 1.0 ); } 87: vec2 saturate( in vec2 a ) { return clamp( a, 0.0, 1.0 ); } 88: vec3 saturate( in vec3 a ) { return clamp( a, 0.0, 1.0 ); } 89: vec4 saturate( in vec4 a ) { return clamp( a, 0.0, 1.0 ); } 90: float average( in float a ) { return a; } 91: float average( in vec2 a ) { return ( a.x + a.y) * 0.5; } 92: float average( in vec3 a ) { return ( a.x + a.y + a.z) / 3.0; } 93: float average( in vec4 a ) { return ( a.x + a.y + a.z + a.w) * 0.25; } 94: float whiteCompliment( in float a ) { return saturate( 1.0 - a ); } 95: vec2 whiteCompliment( in vec2 a ) { return saturate( vec2(1.0) - a ); } 96: vec3 whiteCompliment( in vec3 a ) { return saturate( vec3(1.0) - a ); } 97: vec4 whiteCompliment( in vec4 a ) { return saturate( vec4(1.0) - a ); } 98: vec3 transformDirection( in vec3 normal, in mat4 matrix ) { 99: return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz ); 100: } 101: // http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations 102: vec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) { 103: return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz ); 104: } 105: vec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal) { 106: float distance = dot( planeNormal, point-pointOnPlane ); 107: return point - distance * planeNormal; 108: } 109: float sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) { 110: return sign( dot( point - pointOnPlane, planeNormal ) ); 111: } 112: vec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) { 113: return pointOnLine + lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ); 114: } 115: float calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) { 116: if ( decayExponent > 0.0 ) { 117: return pow( saturate( 1.0 - lightDistance / cutoffDistance ), decayExponent ); 118: } 119: return 1.0; 120: } 121: 122: vec3 inputToLinear( in vec3 a ) { 123: #ifdef GAMMA_INPUT 124: return pow( a, vec3( float( GAMMA_FACTOR ) ) ); 125: #else 126: return a; 127: #endif 128: } 129: vec3 linearToOutput( in vec3 a ) { 130: #ifdef GAMMA_OUTPUT 131: return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) ); 132: #else 133: return a; 134: #endif 135: } 136: 137: #if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) 138: 139: varying vec2 vUv; 140: uniform vec4 offsetRepeat; 141: 142: #endif 143: 144: #ifdef USE_LIGHTMAP 145: 146: varying vec2 vUv2; 147: 148: #endif 149: #if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG ) 150: 151: varying vec3 vReflect; 152: 153: uniform float refractionRatio; 154: 155: #endif 156: 157: #if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP ) 158: 159: varying vec3 vWorldPosition; 160: 161: #endif 162: 163: #ifdef USE_COLOR 164: 165: varying vec3 vColor; 166: 167: #endif 168: #ifdef USE_MORPHTARGETS 169: 170: #ifndef USE_MORPHNORMALS 171: 172: uniform float morphTargetInfluences[ 8 ]; 173: 174: #else 175: 176: uniform float morphTargetInfluences[ 4 ]; 177: 178: #endif 179: 180: #endif 181: #ifdef USE_SKINNING 182: 183: uniform mat4 bindMatrix; 184: uniform mat4 bindMatrixInverse; 185: 186: #ifdef BONE_TEXTURE 187: 188: uniform sampler2D boneTexture; 189: uniform int boneTextureWidth; 190: uniform int boneTextureHeight; 191: 192: mat4 getBoneMatrix( const in float i ) { 193: 194: float j = i * 4.0; 195: float x = mod( j, float( boneTextureWidth ) ); 196: float y = floor( j / float( boneTextureWidth ) ); 197: 198: float dx = 1.0 / float( boneTextureWidth ); 199: float dy = 1.0 / float( boneTextureHeight ); 200: 201: y = dy * ( y + 0.5 ); 202: 203: vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) ); 204: vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) ); 205: vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) ); 206: vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) ); 207: 208: mat4 bone = mat4( v1, v2, v3, v4 ); 209: 210: return bone; 211: 212: } 213: 214: #else 215: 216: uniform mat4 boneGlobalMatrices[ MAX_BONES ]; 217: 218: mat4 getBoneMatrix( const in float i ) { 219: 220: mat4 bone = boneGlobalMatrices[ int(i) ]; 221: return bone; 222: 223: } 224: 225: #endif 226: 227: #endif 228: 229: #ifdef USE_SHADOWMAP 230: 231: varying vec4 vShadowCoord[ MAX_SHADOWS ]; 232: uniform mat4 shadowMatrix[ MAX_SHADOWS ]; 233: 234: #endif 235: #ifdef USE_LOGDEPTHBUF 236: 237: #ifdef USE_LOGDEPTHBUF_EXT 238: 239: varying float vFragDepth; 240: 241: #endif 242: 243: uniform float logDepthBufFC; 244: 245: #endif 246: void main() { 247: #if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) 248: 249: vUv = uv * offsetRepeat.zw + offsetRepeat.xy; 250: 251: #endif 252: #ifdef USE_LIGHTMAP 253: 254: vUv2 = uv2; 255: 256: #endif 257: #ifdef USE_COLOR 258: 259: vColor.xyz = inputToLinear( color.xyz ); 260: 261: #endif 262: #ifdef USE_MORPHNORMALS 263: 264: vec3 morphedNormal = vec3( 0.0 ); 265: 266: morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ]; 267: morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ]; 268: morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ]; 269: morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ]; 270: 271: morphedNormal += normal; 272: 273: #endif 274: #ifdef USE_SKINNING 275: 276: mat4 boneMatX = getBoneMatrix( skinIndex.x ); 277: mat4 boneMatY = getBoneMatrix( skinIndex.y ); 278: mat4 boneMatZ = getBoneMatrix( skinIndex.z ); 279: mat4 boneMatW = getBoneMatrix( skinIndex.w ); 280: 281: #endif 282: #ifdef USE_SKINNING 283: 284: mat4 skinMatrix = mat4( 0.0 ); 285: skinMatrix += skinWeight.x * boneMatX; 286: skinMatrix += skinWeight.y * boneMatY; 287: skinMatrix += skinWeight.z * boneMatZ; 288: skinMatrix += skinWeight.w * boneMatW; 289: skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; 290: 291: #ifdef USE_MORPHNORMALS 292: 293: vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 ); 294: 295: #else 296: 297: vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 ); 298: 299: #endif 300: 301: #endif 302: 303: #ifdef USE_SKINNING 304: 305: vec3 objectNormal = skinnedNormal.xyz; 306: 307: #elif defined( USE_MORPHNORMALS ) 308: 309: vec3 objectNormal = morphedNormal; 310: 311: #else 312: 313: vec3 objectNormal = normal; 314: 315: #endif 316: 317: #ifdef FLIP_SIDED 318: 319: objectNormal = -objectNormal; 320: 321: #endif 322: 323: vec3 transformedNormal = normalMatrix * objectNormal; 324: 325: #ifndef FLAT_SHADED 326: vNormal = normalize( transformedNormal ); 327: #endif 328: #ifdef USE_MORPHTARGETS 329: 330: vec3 morphed = vec3( 0.0 ); 331: morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ]; 332: morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ]; 333: morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ]; 334: morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ]; 335: 336: #ifndef USE_MORPHNORMALS 337: 338: morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ]; 339: morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ]; 340: morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ]; 341: morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ]; 342: 343: #endif 344: 345: morphed += position; 346: 347: #endif 348: #ifdef USE_SKINNING 349: 350: #ifdef USE_MORPHTARGETS 351: 352: vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 ); 353: 354: #else 355: 356: vec4 skinVertex = bindMatrix * vec4( position, 1.0 ); 357: 358: #endif 359: 360: vec4 skinned = vec4( 0.0 ); 361: skinned += boneMatX * skinVertex * skinWeight.x; 362: skinned += boneMatY * skinVertex * skinWeight.y; 363: skinned += boneMatZ * skinVertex * skinWeight.z; 364: skinned += boneMatW * skinVertex * skinWeight.w; 365: skinned = bindMatrixInverse * skinned; 366: 367: #endif 368: 369: #ifdef USE_SKINNING 370: 371: vec4 mvPosition = modelViewMatrix * skinned; 372: 373: #elif defined( USE_MORPHTARGETS ) 374: 375: vec4 mvPosition = modelViewMatrix * vec4( morphed, 1.0 ); 376: 377: #else 378: 379: vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); 380: 381: #endif 382: 383: gl_Position = projectionMatrix * mvPosition; 384: 385: #ifdef USE_LOGDEPTHBUF 386: 387: gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC; 388: 389: #ifdef USE_LOGDEPTHBUF_EXT 390: 391: vFragDepth = 1.0 + gl_Position.w; 392: 393: #else 394: 395: gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w; 396: 397: #endif 398: 399: #endif 400: vViewPosition = -mvPosition.xyz; 401: #if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP ) 402: 403: #ifdef USE_SKINNING 404: 405: vec4 worldPosition = modelMatrix * skinned; 406: 407: #elif defined( USE_MORPHTARGETS ) 408: 409: vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 ); 410: 411: #else 412: 413: vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); 414: 415: #endif 416: 417: #endif 418: 419: #if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG ) 420: 421: vec3 worldNormal = transformDirection( objectNormal, modelMatrix ); 422: 423: vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); 424: 425: #ifdef ENVMAP_MODE_REFLECTION 426: 427: vReflect = reflect( cameraToVertex, worldNormal ); 428: 429: #else 430: 431: vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); 432: 433: #endif 434: 435: #endif 436: 437: #if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP ) 438: 439: vWorldPosition = worldPosition.xyz; 440: 441: #endif 442: #ifdef USE_SHADOWMAP 443: 444: for( int i = 0; i < MAX_SHADOWS; i ++ ) { 445: 446: vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition; 447: 448: } 449: 450: #endif 451: } (qrc:/three.js:68, ) Warning: QOpenGLShader::link: "Attached vertex shader is not compiled.\n" (opengl\qopenglshaderprogram.cpp:1049, virtual bool QOpenGLShaderProgram::link()) Warning: CanvasRenderer::executeCommandQueue: Failed to link program: QOpenGLShaderProgram(0x2f6746a0) (canvasrenderer.cpp:1252, void QtCanvas3D::CanvasRenderer::executeCommandQueue()) Critical: THREE.WebGLProgram: shader error: 1281 gl.VALIDATE_STATUS false gl.getPRogramInfoLog Attached vertex shader is not compiled. (qrc:/three.js:69, ) Warning: THREE.WebGLProgram: gl.getProgramInfoLog()Attached vertex shader is not compiled. (qrc:/three.js:68, ) Warning: QOpenGLShaderProgram::uniformLocation( viewMatrix ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( modelViewMatrix ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( projectionMatrix ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( normalMatrix ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( modelMatrix ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( cameraPosition ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( morphTargetInfluences ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( bindMatrix ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( bindMatrixInverse ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( boneGlobalMatrices ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( alphaMap ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( diffuse ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( envMap ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( flipEnvMap ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( lightMap ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( map ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( morphTargetInfluences ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( offsetRepeat ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( opacity ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( reflectivity ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( refractionRatio ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( specularMap ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( bumpMap ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( bumpScale ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( normalMap ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( normalScale ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( fogColor ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( fogDensity ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( fogFar ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( fogNear ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( ambientLightColor ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( directionalLightColor ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( directionalLightDirection ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( hemisphereLightDirection ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( hemisphereLightGroundColor ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( hemisphereLightSkyColor ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( pointLightColor ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( pointLightDecay ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( pointLightDistance ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( pointLightPosition ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( spotLightAngleCos ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( spotLightColor ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( spotLightDecay ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( spotLightDirection ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( spotLightDistance ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( spotLightExponent ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( spotLightPosition ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( shadowBias ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( shadowDarkness ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( shadowMap ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( shadowMapSize ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( shadowMatrix ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( emissive ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( shininess ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( specular ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::uniformLocation( wrapRGB ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1890, int QOpenGLShaderProgram::uniformLocation(const char*) const) Warning: QOpenGLShaderProgram::attributeLocation( position ): shader program is not linked (opengl\qopenglshaderprogram.cpp:1214, int QOpenGLShaderProgram::attributeLocation(const char*) const) Warning: CanvasRenderer::bindCurrentRenderTarget: OpenGL ERROR: 1282 (canvasrenderer.cpp:639, bool QtCanvas3D::CanvasRenderer::updateGlError(const char*))
上述是问题日志输出,由于在独立显卡的openlg4.1版本运行正常,opengl4.0版本的平板运行失败,目前平板的集显,opengl4.0,windows10,很无奈,不知道为什么希望有大神能够指点迷津。模型使用blender转json,io_three多个版本都实验的,不成功。
人工智能
2018-10-08 15:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
人工智能技术的透明度和道德伦理问题,正引发越来越多的关注,这促使云计算服务提供商推出新工具,解释人工智能算法背后的决策过程。
会计和金融等强监管行业的高管表示,数据科学家和非技术业务经理都必须能理解算法决策背后的流程,这至关重要。这样的理解在防范潜在道德违规和监管违规方面可能会带来深远影响,尤其考虑到企业级人工智能算法正变得越来越普遍。
毕马威创新和企业解决方案部门负责智能自动化、认知和人工智能的高管维诺德·斯瓦米纳桑(Vinodh Swaminathan)表示:“我认为,除非具备这种解释能力,否则人工智能在企业中的规模不可能超过数百个试点应用。”
对人工智能的解释问题已经促使IBM和谷歌等公司在云计算人工智能服务产品中引入透明度和道德伦理工具。比如,IBM商业价值研究所近期的一项研究调查了5000名企业高管。约60%的受访者表示,他们关心如何解释人工智能使用数据作出决策,以达到监管和合规标准。这个比例较2016年时的29%大幅上升。
甚至对数据科学家和相关企业高管来说,人工智能的决策过程有时都是“黑盒”。在深度学习工具,例如用于模式识别的神经网络中尤其如此。这样的神经网络试图模拟人脑的运转方式。尽管这些系统可以以前所未有的准确率和速度得出结论,但外界并不总是非常清楚,计算网络是如何做出具体的决策。
毕马威内部的数据科学家正在开发自主的可解释性工具,此外该公司也在利用IBM新的透明度工具。斯瓦米纳桑表示,这样做的目的是确保技术和业务两方面的员工都能“打开黑盒”,准确地了解人工智能算法是如何做出结论的。
IBM上周发布了新的云计算人工智能工具,可以向用户展示哪些主要因素影响了人工智能做出的建议。这些工具还可以实时分析人工智能决策,以识别固有偏见,并推荐数据和方法来解决这些偏见。IBM负责认知解决方案的高级副总裁大卫·肯尼(David Kenny)表示,这些工具可以与IBM人工智能服务,以及谷歌等其他云计算服务提供商的工具配合使用。
人工智能
2018-10-08 10:37:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
损失函数可以有两种 1.点到直线的竖直距离 2.点到直线的垂直距离 import tensorflow as tf import matplotlib.pyplot as plt import numpy as np from sklearn import datasets
sess = tf.Session()
iris = datasets.load_iris()
x_vals = np.array([x[3] for x in iris.data]) y_vals = np.array([y[0] for y in iris.data])
learn_rate = 0.05 batchsize = 50 x_data = tf.placeholder(shape=[None,1],dtype=tf.float32) y_target = tf.placeholder(shape=[None,1],dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1,1])) b = tf.Variable(tf.random_normal(shape=[1,1])) demming_upper = tf.abs(tf.subtract(y_target,tf.add(tf.matmul(x_data,A),b))) demming_down = tf.sqrt(tf.add(tf.square(A),1))
loss = tf.reduce_mean(tf.truediv(demming_upper,demming_down))
init = tf.global_variables_initializer() sess.run(init)
my_opt = tf.train.GradientDescentOptimizer(0.1) train_step = my_opt.minimize(loss)
loss_dev = []
for i in range(250): rand_index = np.random.choice(len(x_vals), size=batchsize) rand_x = np.transpose([x_vals[rand_index]]) rand_y = np.transpose([y_vals[rand_index]]) sess.run(train_step,feed_dict={x_data:rand_x,y_target:rand_y}) temp_loss = sess.run(loss,feed_dict={x_data:rand_x,y_target:rand_y}) loss_dev.append(temp_loss)
[slope] = sess.run(A) [intercept] = sess.run(b)
best_line = [] for x in x_vals: best_line.append(slope*x+intercept)
plt.plot(x_vals,y_vals,'bo',label = 'data') plt.plot(x_vals,best_line,'r--',label = 'best line') plt.legend(loc = 'upper right') plt.show()
plt.plot(loss_dev) plt.show()
人工智能
2018-10-07 16:29:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
import matplotlib.pyplot as plt import numpy as np import tensorflow as tf
sess = tf.Session() x_vals = np.linspace(0,10,100) y_vals = x_vals+np.random.normal(0,1,100) x_vals_column = np.transpose(np.matrix(x_vals)) one_clumn = np.transpose(np.matrix(np.repeat(1,100))) A = np.column_stack((x_vals_column,one_clumn)) b =np.transpose(np.matrix(y_vals))
A_tensor = tf.constant(A) b_tensor = tf.constant(b)
tA_A = tf.matmul(tf.transpose(A_tensor),A_tensor)
tA_A_inv = tf.matrix_inverse(tA_A) product = tf.matmul(tA_A_inv,tf.transpose(A_tensor)) solution = tf.matmul(product,b_tensor)
solution_eval = sess.run(solution) print(solution_eval)
slope = solution_eval[0][0] y_intercept = solution_eval[1][0]
best_fit =[] for i in x_vals: best_fit.append(slope*i+y_intercept) plt.plot(x_vals,y_vals,'o',label = 'data') plt.plot(x_vals,best_fit,'r--',label='best fit line') plt.legend(loc = 'upper left') plt.show()
人工智能
2018-10-06 19:16:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
import tensorflow as tf import matplotlib.pyplot as plt import numpy as np from sklearn import datasets from tensorflow.python.framework import ops ops.reset_default_graph() sess = tf.Session()
iris = datasets.load_iris()
x_vals = np.array([x[3] for x in iris.data]) y_vals = np.array([y[0] for y in iris.data])
learn_rate = 0.05 batch_size = 25 x_data = tf.placeholder(shape=[None,1],dtype=tf.float32) y_target = tf.placeholder(shape=[None,1],dtype=tf.float32)
A = tf.Variable(tf.random_normal(shape=[1,1])) b = tf.Variable(tf.random_normal(shape=[1,1]))
model_output = tf.add(tf.matmul(x_data,A),b)
loss = tf.reduce_mean(tf.square(y_target-model_output)) init = tf.global_variables_initializer() sess.run(init) my_opt = tf.train.GradientDescentOptimizer(learn_rate) train_step = my_opt.minimize(loss)
loss_vec = []
for i in range(200): rand_index = np.random.choice(len(x_vals),size=batch_size) rand_x = np.transpose([x_vals[rand_index]]) rand_y = np.transpose([y_vals[rand_index]]) sess.run(train_step,feed_dict={x_data:rand_x,y_target:rand_y}) temp_loss = sess.run(loss,feed_dict={x_data:rand_x,y_target:rand_y}) loss_vec.append(temp_loss)
[slope] = sess.run(A) [intercept] = sess.run(b) best_fit =[] for i in x_vals: best_fit.append(i*slope+intercept) plt.plot(x_vals,y_vals,'x',label='data') plt.plot(x_vals,best_fit,'r--',label='best line') plt.legend(loc ='upper right') plt.show() plt.plot(loss_vec,'k-') plt.show()
人工智能
2018-10-06 19:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
https://blog.csdn.net/gavinmiaoc/article/details/80802967
深度学习的Image Inpainting (图像修复)论文推荐(持续更新)

https://blog.csdn.net/gavinmiaoc/article/details/80802967
人工智能
2018-10-04 14:09:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 定义:采用测量不同特征值之间的距离方法进行分类。 优点:精度高,对异常值不敏感,无数据输入假定。 缺点:计算复杂度高,空间度高。 适用数据范围: 数值型和标称型。 工作原理: 存在一个样本数据集合,并且样本中每个数据都存在标签,输入没有标签的数据后,将新数据的每个特征值与样本集中的每个特征值比对,然后算法提取样本中特征值最相似数据(最近邻)的分类标签。最后选择k个最相似数据中出现次数最多的分类,作为最新的分类。
人工智能
2018-10-04 10:17:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
import matplotlib.pyplot as plt import numpy as np import pandas as pd from mpl_toolkits.mplot3d import Axes3D
t = np.arange(0,5,0.2)
plt.plot(t,t**2,'g^')
plt.ylabel('nubers')
plt.xlabel('seed')
plt.title('title')
plt.grid(True)
plt.yscale('log')
plt.show()
#sinx的函数图
t = np.arange(0,2.0,0.01)
s= np.sin(2 * np.pi*t)
fig,ax = plt.subplots()
ax.set_ylabel('sin')
ax.set_xlabel('df')
ax.plot(t,s)
plt.grid(True)
plt.show()
#饼状图
labels ='frogs','hogs','dogs','logs'
sizes =[15,45,10,30]
explode=(0,0.1,0,0)
fig,ax = plt.subplots()
ax.pie(sizes,explode=explode,labels=labels,autopct='%1.lf%%',
shadow=True,startangle=90)
plt.show()
#ax.scatter表示散点图
#化三维图形
fig,ax = plt.subplots()
fig = plt.figure()
ax = fig.add_subplot(111,projection ='3d')
x,y=np.random.rand(2,100)*4
ax.bar3d(x,y,x,x,y,x**y)
plt.show()
df=pd.DataFrame(np.random.randn(1000,4), index=pd.date_range('1/1/2000',periods=1000),columns=list('abcd'))
df =df.cumsum()
plt.show()
df = pd.DataFrame(np.random.randn(5,4),index=['A','B','C','D','E'],columns=pd.Index([1,2,3,4])) df.plot(kind='bar') plt.show()
import numpy as np data = np.array([[1,2,3],[3,4,5]]) data = np.zeros((2,3)) data1 = np.empty((2,3)) data2 = np.arange(3,6,2) data3 = np.random.rand(2,3,) data4 = np.arange(100) data5 = np.arange(0,20,2) data5[1:4:2]=1 data6 = np.array([[1,2,3],[4,5,6],[7,8,9]]) data6.sum(axis=1) data6.min(1) print(data6.std())#计算标准差 print(data6.var())#计算方差
人工智能
2018-09-30 21:01:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
simple-car-plate-recognition
简单车牌识别-Mask_RCNN定位车牌+手写方法分割字符+CNN单个字符识别
数据准备
准备用于车牌定位的数据集,要收集250张车辆图片,200张用于训练,50张用于测试,然后在这些图片上标注出车牌区域。这里有图片 https://gitee.com/easypr/EasyPR/tree/master/resources/image/general_test 。标注工具使用VGG Image Annotator (VIA),就是一个网页程序,可以导入图片,使用多边形标注,标注好了以后,导出json。我已经标注好的数据集可以从这里下载 https://github.com/airxiechao/simple-car-plate-recognition/blob/master/dataset/carplate.zip ,用7zip解压。
准备用于字符识别的数据集,包含分隔好的单个车牌汉子、字母和数字。这里有 https://gitee.com/easypr/EasyPR/blob/master/resources/train/ann.7z 。
训练Mask-RCNN定位车牌
这篇文章 https://engineering.matterport.com/splash-of-color-instance-segmentation-with-mask-r-cnn-and-tensorflow-7c761e238b46 讲了如何用Mask-RCNN识别图片中的气球,仿照其方法。在 https://github.com/matterport/Mask_RCNN/releases 下载预先用COCO数据集训练好的模型mask_rcnn_coco.h5,按照文章的方法编写carplate.py用于载入车辆图片数据和训练,用inspect_data.ipynb浏览标注数据。执行python carplate.py train --dataset=../dataset/carplate --weights=coco 进行训练,训练完后,在logs文件夹中找到最后一轮的h5模型文件,比如mask_rcnn_carplate_0030.h5,复制出来。用inspect_model.ipynd查看模型训练的效果。这部分代码在 https://github.com/airxiechao/simple-car-plate-recognition/tree/master/Mask_RCNN 下载。
训练CNN单个字符识别
仿照keras的mnist_cnn例子 https://github.com/keras-team/keras/blob/master/examples/mnist_cnn.py 训练,将训练好的模型导出为char_cnn.h5。这部分代码在 https://github.com/airxiechao/simple-car-plate-recognition/blob/master/char_cnn/char_cnn.ipynb 下载。
分割车牌字符
把车牌区域转换成灰度图像,利用边缘特征分割出区域,再筛选出字符区域。字符分割代码是 https://github.com/airxiechao/simple-car-plate-recognition/blob/master/character_segmentation.ipynb 。
执行推理
代码在 https://github.com/airxiechao/simple-car-plate-recognition/blob/master/inference.ipynb 。
人工智能
2018-10-10 14:52:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
项目的目的
在做问答系统研究的时候,想通过deeplearning的方法获得句子语义,并计算两个问句的相似度,为此需要相似问题的数据集,但是一般相似问题数据集很难获取,特别是质量较高的数据集。为此,想到用目前最先进的翻译系统来实现构建数据。
另外,当前NLP比较成功应用就是机器翻译,之所以deeplearning能成功应用到翻译,得意于庞大的、高质量的翻译语料数据。
1.爬取goole翻译
说明
谷歌翻译直接通过request请求是获取不到结果的,需要tk值,tk值需要由问句+tkk值来生成。 获取tkk:
requests获取主页面,只需re正则在主页面上获取tkk值(之前需要通过js脚本来实现, 参考 ) 获取tk
通过js脚本实现:gettk.js var b = function (a, b) { for (var d = 0; d < b.length - 2; d += 3) { var c = b.charAt(d + 2), c = "a" <= c ? c.charCodeAt(0) - 87 : Number(c), c = "+" == b.charAt(d + 1) ? a >>> c : a << c; a = "+" == b.charAt(d) ? a + c & 4294967295 : a ^ c } return a } var tk = function (a,TKK) { //console.log(a,TKK); for (var e = TKK.split("."), h = Number(e[0]) || 0, g = [], d = 0, f = 0; f < a.length; f++) { var c = a.charCodeAt(f); 128 > c ? g[d++] = c : (2048 > c ? g[d++] = c >> 6 | 192 : (55296 == (c & 64512) && f + 1 < a.length && 56320 == (a.charCodeAt(f + 1) & 64512) ? (c = 65536 + ((c & 1023) << 10) + (a.charCodeAt(++f) & 1023), g[d++] = c >> 18 | 240, g[d++] = c >> 12 & 63 | 128) : g[d++] = c >> 12 | 224, g[d++] = c >> 6 & 63 | 128), g[d++] = c & 63 | 128) } a = h; for (d = 0; d < g.length; d++) a += g[d], a = b(a, "+-a^+6"); a = b(a, "+-3^+b+-f"); a ^= Number(e[1]) || 0; 0 > a && (a = (a & 2147483647) + 2147483648); a %= 1E6; return a.toString() + "." + (a ^ h) } 最终地址
将tk值和需要翻译的句子代人如下格式
中-英 : https://translate.google.cn/translate_a/single?client=t&sl=zh-CN&tl=en&hl=zh-CN&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&ie=UTF-8&oe=UTF-8&source=bh&ssel=0&tsel=0&kc=1&tk=xxxxxx&q=xxxxxxx
英-中: https://translate.google.cn/translate_a/single?client=t&sl=en&tl=zh-CN&hl=zh-CN&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&ie=UTF-8&oe=UTF-8&source=bh&ssel=0&tsel=0&kc=1&tk=xxxxxx&q=xxxxxxx
程序中简单增加了中英文判断。 requests获取结果
获取的结果在三维的列表中,list[0][0][0]即为所需的结果
程序 goole_trans.py import requests import urllib import re import json import execjs class Goole_translate(): def __init__(self): self.url_base = 'https://translate.google.cn' self.headers = {'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36'} self.get_tkk() def get_tkk(self): page = requests.get(self.url_base, headers= self.headers ) tkks = re.findall(r"TKK='(.+?)';", page.text) if tkks: self.tkk = tkks[0] return self.tkk else: raise ('no found tkk') def translate(self, query_string): last_url = self.get_last_url(query_string) response = requests.get(last_url, headers=self.headers) if response.status_code != 200: self.get_tkk() last_url = self.get_last_url(query_string) response = requests.get(last_url, headers=self.headers) content = response.content # bytes类型 text = content.decode() # str类型 , 两步可以用text=response.text替换 dict_text = json.loads(text) # 数据是json各式 result = dict_text[0][0][0] return result def get_tk(self, query_string): tem = execjs.compile(open(r"gettk.js").read()) tk = tem.call('tk', query_string, self.tkk) return tk def query_string(self, query_string): '''将字符串转换为utf8格式的字符串,本身已utf8格式定义的字符串可以不需要''' query_url_trans = urllib.parse.quote(query_string) # 汉字url编码, 转为utf-8各式 return query_url_trans def get_last_url(self, query_string): url_parm = 'sl=en&tl=zh-CN' for uchar in query_string: if uchar >= u'一' and uchar <= u'龥': url_parm = 'sl=zh-CN&tl=en' break url_param_part = self.url_base + "/translate_a/single?" url_param = url_param_part + "client=t&"+ url_parm+"&hl=zh-CN&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&ie=UTF-8&oe=UTF-8&source=btn&ssel=3&tsel=3&kc=0&" url_get = url_param + "tk=" + str(self.get_tk(query_string)) + "&q=" + str(self.query_string(query_string)) return url_get if __name__=="__main__": query_string = 'how are you' gt = Goole_translate() en = gt.translate(query_string) print(en)
注意
频繁访问可能被封,没有测试过,可以设置延时或ip代理
参考
http://www.cnblogs.com/by-dream/p/6554340.html
https://blog.csdn.net/boyheroes/article/details/78681357
2.爬取百度翻译 自动检测中英文 获取百度翻译结果
程序 baidu_trans.py # coding=utf-8 import requests import json class Baidu_translate(): def __init__(self): self.headers = {"User-Agent":"Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Mobile Safari/537.36"} self.lang_detect_url = "https://fanyi.baidu.com/langdetect" self.trans_url = "https://fanyi.baidu.com/basetrans" def get_lang(self,query_string): '''自动检测语言''' data = {'query':query_string} response = requests.post(self.lang_detect_url, data=data, headers=self.headers) return json.loads(response.text)['lan'] def translate(self,query_string): '''翻译''' lang = self.get_lang(query_string) data = {"query":query_string,"from":"zh","to":"en"} if lang== "zh" else {"query":query_string,"from":"en","to":"zh"} response = requests.post(self.trans_url, data=data, headers=self.headers) result = json.loads(response.text)["trans"][0]["dst"] return result if __name__ == '__main__': query_string = 'how are you' bt = Baidu_translate() en = bt.translate(query_string) print(en)
参考
https://blog.csdn.net/blues_f/article/details/79319461
3.中文-英文-中文
通过中-英-中可以产生相似问答对语料。 q_zh:中文问句 g_en:谷歌对中文问句中-英翻译 b_zh:百度对谷歌结果进行英-中翻译
程序 zh_en_zh_translate.py ''' 通过翻译实现中文问句的相似问法,来产生相似问题对数据集。可用于语义相似模型训练。 goole翻译:中文-英文 baidu翻译:英文-中文 注意:本程序未设置ip代理,频繁访问谨防被封。(只做了简单的随机延迟措施) ''' import time import random from goole_trans import Goole_translate from baidu_trans import Baidu_translate gt = Goole_translate() bt = Baidu_translate() r_file = 'data/zh.txt' w_file = 'data/zh_en_zh.txt' fw = open(w_file,'w',encoding='utf8') with open(r_file,'r',encoding='utf8') as f: for line in f: r = random.random()*10 time.sleep(r) ls = line.strip().split('\t') query_string = ls[0] g_en = gt.translate(query_string) b_zh = bt.translate(g_en) fw.write(query_string+'\t'+g_en+'\t'+b_zh+'\n') print('q_zh:',query_string) print('g_en:',g_en) print('b_zh:',b_zh) print('\n') fw.close()
运行结果 q_zh: 下周有什么好产品? g_en: What are the good products next week? b_zh: 下周的好产品是什么? ------------ q_zh: 第一次使用,额度多少? g_en: What is the amount of the first use? b_zh: 第一次使用的数量是多少? ------------ q_zh: 我什么时候可以通过微粒贷借钱 g_en: When can I borrow money from micro-credit? b_zh: 我什么时候可以从小额信贷中借钱? ------------ q_zh: 借款后多长时间给打电话 g_en: How long does it take to make a call after borrowing? b_zh: 借钱后打电话需要多长时间? ------------ q_zh: 没看到微粒贷 g_en: Didn't see the micro-credit b_zh: 没有看到小额信贷 ------------ q_zh: 原来的手机号不用了,怎么换 g_en: The original mobile phone number is not used, how to change b_zh: 原来的手机号码没有用,怎么改
注意
程序均未设置ip代理,频繁访问谨防被封。(只做了简单的随机延迟措施),后期若更新,见github: github地址
人工智能
2018-09-25 18:00:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
使用方式如下: 类名::方法名
方法名后面没有括号“()”。Lambda有懒加载嘛,不要括号就是说,看情况调用方法。
person -> person.getAge();
可以替换成
Person::getAge
人工智能
2018-09-25 16:48:00