注册
环信即时通讯云

环信即时通讯云

单聊、群聊、聊天室...
环信开发文档

环信开发文档

Demo体验

Demo体验

场景Demo,开箱即用
RTE开发者社区

RTE开发者社区

汇聚音视频领域技术干货,分享行业资讯
技术讨论区

技术讨论区

技术交流、答疑
资源下载

资源下载

收集了海量宝藏开发资源
iOS Library

iOS Library

不需要辛辛苦苦的去找轮子, 这里都有
Android Library

Android Library

不需要辛辛苦苦的去找轮子, 这里都有

李理:自动梯度求解——cs231n的notes

本系列文章面向深度学习研发者,希望通过Image Caption Generation,一个有意思的具体任务,深入浅出地介绍深度学习的知识。本系列文章涉及到很多深度学习流行的模型,如CNN,RNN/LSTM,Attention等。本文为第5篇。   作者:...
继续阅读 »
本系列文章面向深度学习研发者,希望通过Image Caption Generation,一个有意思的具体任务,深入浅出地介绍深度学习的知识。本系列文章涉及到很多深度学习流行的模型,如CNN,RNN/LSTM,Attention等。本文为第5篇。
 
作者:李理 
目前就职于环信,即时通讯云平台和全媒体智能客服平台,在环信从事智能客服和智能机器人相关工作,致力于用深度学习来提高智能机器人的性能。
相关文章: 
环信李理:从Image Caption Generation了解深度学习
李理:从Image Caption Generation理解深度学习(part II)
李理:从Image Caption Generation理解深度学习(part III)
李理:自动梯度求解 反向传播算法的另外一种视角
 
Optimization

这一部分内容来自:CS231n Convolutional Neural Networks for Visual Recognition

简介

我们的目标:x是一个向量,f(x)是一个函数,它的输入是一个向量(或者认为是多变量的函数,这个输入向量就是自变量),输出是一个实数值。我们需要计算的是f对每一个自变量的导数,然后把它们排成一个向量,也就是梯度。

001.jpg


为什么要求这个呢?前面我们也讲了,我们的神经网络的损失函数最终可以看成是权重weights和bias的函数,我们的目标就是调整这些参数,使得损失函数最小。

简单的表达式和梯度的解释

首先我们看一个很简单的函数 f(x,y)=xy,求f对x和y的偏导数很简单:

002.jpg


首先来看导数的定义:

003.jpg


函数在某个点的导数就是函数曲线在这个点的斜率,也就是f(x)随x的变化率。 
比如上面的例子,当x=4,y=−3时 f(x,y)=−12,f对x的偏导数

004.jpg


也就是说,如果我们固定y=4,然后给x一个很小的变化h,那么f(x,y)的变化大约是-3*h。 
因此乘法的梯度就是

005.jpg


同样,加法的梯度更简单:

006.jpg


最后一个简单函数是max函数:

007.jpg


这个导数是ReLU(x)=max(x,0)的导数,其实也简单,如果 x>=y,那么 max(x,y)=x,则导数是1,否则 max(x,y)=0,那么对x求导就是0。

复杂表达式的链式法则

接下来看一个稍微复杂一点的函数 f(x,y,z)=(x+y)z。我们引入一个中间变量q,f=qz,q=x+y,我们可以使用链式法则求f对x和y的导数。

008.jpg


对y的求导也是类似的。

下面是用python代码来求f对x和y的导数在某一个点的值。
# 设置自变量的值
x = -2; y = 5; z = -4

# “前向”计算f
q = x + y # q becomes 3
f = q * z # f becomes -12

# 从“后”往前“反向”计算
# 首先是 f = q * z
dfdz = q # 因为df/dz = q, 所以f对z的梯度是 3
dfdq = z # 因为df/dq = z, 所以f对q的梯度是 -4
# 然后 q = x + y
dfdx = 1.0 * dfdq # 因为dq/dx = 1,所以使用链式法则计算dfdx=-4
dfdy = 1.0 * dfdq # 因为dq/dy = 1,所以使用链式法则计算dfdy=-4
我们也可以用计算图来表示和计算:

009.jpg


绿色的值是feed forward的结果,而红色的值是backprop的结果。

不过我觉得cs231n课程的这个图没有上面blog的清晰,原因是虽然它标示出来了最终的梯度,但是没有标示出local gradient,我在下面会画出完整的计算过程。

反向传播算法的直觉解释

我们如果把计算图的每一个点看成一个“门”(或者一个模块),或者说一个函数。它有一个输入(向量),也有一个输出(标量)。对于一个门来说有两个计算,首先是根据输入,计算输出,这个一般很容易。还有一种计算就是求输出对每一个输入的偏导数,或者说输出对输入向量的”局部“梯度(local gradient)。一个复杂计算图(神经网络)的计算首先就是前向计算,然后反向计算,反向计算公式可能看起来很复杂,但是如果在计算图上其实就是简单的用local gradient乘以从后面传过来的gradient,然后加起来。

Sigmoid模块的例子

接下来我们看一个更复杂的例子:

010.jpg


这个函数是一个比较复杂的复合函数,但是构成它的基本函数是如下4个简单函数:

011.jpg


下面是用计算图画出这个计算过程:

012.jpg


这个图有4种gate,加法,乘法,指数和倒数。加法有加一个常数和两个变量相加,乘法也是一样。

上图绿色的值是前向计算的结果,而红色的值是反向计算的结果,local graident并没有标示出来,所以看起来可能有些跳跃,下面我在纸上详细的分解了其中的步骤,请读者跟着下图自己动手计算一遍。

013.jpg


上图就是前向计算的过程,比较简单。

014.jpg


第二个图是计算local gradient,对于两个输入的乘法和加法,local gradient也是两个值,local gradient的值我是放到图的节点上了。

015.jpg


第三个图是具体计算一个乘法的local gradient的过程,因为上图可能看不清,所以单独放大了这一步。

016.jpg


最后计算真正的梯度,是把local gradient乘以来自上一步的gradient。不过这个例子一个节点只有一个输出,如果有多个的话,梯度是加起来的,可以参考1.4的

017.jpg


上面我们看到把

018.jpg


分解成最基本的加法,乘法,导数和指数函数,但是我们也可以不分解这么细。之前我们也学习过了sigmoid函数,那么我们可以这样分解:

019.jpg


σ(x)σ(x) 的导数我们之前已经推导过一次了,这里再列一下:

020.jpg


因此我们可以把后面一长串的gate”压缩“成一个gate:

021.jpg


我们来比较一下,之前前向计算 σ(x)σ(x) 需要一次乘法,一次exp,一次加法导数;而反向计算需要分别计算这4个gate的导数。

而压缩后前向计算是一样的,但是反向计算可以”利用“前向计算的结果

022.jpg


这只需要一次减法和一次乘法!当然如果不能利用前向的结果,我们如果需要重新计算 σ(x)σ(x) ,那么压缩其实没有什么用处。能压缩的原因在于σ函数导数的特殊形式。而神经网络的关键问题是在训练,训练性能就取决于这些细节。如果是我们自己来实现反向传播算法,我们就需要利用这样的特性。而如果是使用工具,那么就依赖于工具的优化水平了。

下面我们用代码来实现一下:
w = [2,-3,-3] # assume some random weights and data
x = [-1, -2]

# forward pass
dot = w[0]*x[0] + w[1]*x[1] + w[2]
f = 1.0 / (1 + math.exp(-dot)) # sigmoid function

# backward pass through the neuron (backpropagation)
ddot = (1 - f) * f # gradient on dot variable, using the sigmoid gradient derivation
dx = [w[0] * ddot, w[1] * ddot] # backprop into x
dw = [x[0] * ddot, x[1] * ddot, 1.0 * ddot] # backprop into w
# we're done! we have the gradients on the inputs to the circuit
上面的例子用了一个小技巧,就是所谓的staged backpropagation,说白了就是给中间的计算节点起一个名字。比如dot。为了让大家熟悉这种技巧,下面有一个例子。

Staged computation练习

023.jpg


我们用代码来计算这个函数对x和y的梯度在某一点的值

前向计算
x = 3 # example values
y = -4

# forward pass
sigy = 1.0 / (1 + math.exp(-y)) # 分子上的sigmoid #(1)
num = x + sigy # 分子 #(2)
sigx = 1.0 / (1 + math.exp(-x)) # 分母上的sigmoid #(3)
xpy = x + y #(4)
xpysqr = xpy**2 #(5)
den = sigx + xpysqr # 分母 #(6)
invden = 1.0 / den #(7)
f = num * invden # done! #(8)
反向计算
# backprop f = num * invden
dnum = invden # gradient on numerator #(8)
dinvden = num #(8)
# backprop invden = 1.0 / den
dden = (-1.0 / (den**2)) * dinvden #(7)
# backprop den = sigx + xpysqr
dsigx = (1) * dden #(6)
dxpysqr = (1) * dden #(6)
# backprop xpysqr = xpy**2
dxpy = (2 * xpy) * dxpysqr #(5)
# backprop xpy = x + y
dx = (1) * dxpy #(4)
dy = (1) * dxpy #(4)
# backprop sigx = 1.0 / (1 + math.exp(-x))
dx += ((1 - sigx) * sigx) * dsigx # Notice += !! See notes below #(3)
# backprop num = x + sigy
dx += (1) * dnum #(2)
dsigy = (1) * dnum #(2)
# backprop sigy = 1.0 / (1 + math.exp(-y))
dy += ((1 - sigy) * sigy) * dsigy #(1)
# done! phew
需要注意的两点:1. 前向的结果都要保存下来,反向的时候要用的。2. 如果某个变量有多个出去的边,第一次是等于,第二次就是+=,因为我们要把不同出去点的梯度加起来。

下面我们来逐行分析反向计算: 
(8) f = num * invden 
local gradient

024.jpg


而上面传过来的梯度是1,所以 dnum=1∗invden。注意变量的命名规则, df/dnum就命名为dnum【省略了df,因为默认我们是求f对所有变量的偏导数】 
同理: dinvden=num

(7) invden = 1.0 / den

local gradient是 (−1.0/(den∗∗2)) ,然后乘以上面来的dinvden

(6) den = sigx + xpysqr

这个函数有两个变量sigx和xpysqr,所以需要计算两个local梯度,然后乘以dden 
加法的local梯度是1,所以就是(1)*dden

(5) xpysqr = xpy**2

local gradient是2*xpy,再乘以dxpysqr

(4) xpy = x + y

还是一个加法,local gradient是1,所以dx和dy都是dxpy乘1

(3) sigx = 1.0 / (1 + math.exp(-x))

这是sigmoid函数,local gradient是 (1-sigx)*sigx,再乘以dsigx。 
不过需要注意的是这是dx的第二次出现,所以是+=,表示来自不同路径反向传播过来给x的梯度值

(2) num = x + sigy

还是个很简单的加法,local gradient是1。需要注意的是dx是+=,理由同上。

(1) sigy = 1.0 / (1 + math.exp(-y))

最后是sigmoid(y)和前面(3)一样的。

请仔细阅读上面反向计算的每一步代码,确保自己理解了之后再往下阅读。

梯度的矩阵运算

前面都是对一个标量的计算,在实际实现时用矩阵运算一次计算一层的所有梯度会更加高效。因为矩阵乘以向量和向量乘以向量都可以看出矩阵乘以矩阵的特殊形式,所以下面我们介绍矩阵乘法怎么求梯度。

首先我们得定义什么叫矩阵对矩阵的梯度!

我查阅了很多资料,也没找到哪里有矩阵对矩阵的梯度的定义,如果哪位读者知道,请告诉我,谢谢!唯一比较接近的是Andrew Ng的课程cs294的背景知识介绍的slides linalg的4.1节定义了gradient of Matrix,关于矩阵对矩阵的梯度我会有一个猜测性的解释,可能会有问题。

首先介绍graident of matrix

假设 f:Rm×n→R是一个函数,输入是一个m×n的实数值矩阵,输出是一个实数。那么f对A的梯度是如下定义的:

025.jpg


看起来定义很复杂?其实很简单,我们把f看成一个mn个自变量的函数,因此我们可以求f对这mn个自变量的偏导数,然后把它们排列成m*n的矩阵就行了。为什么要多此一举把变量拍成矩阵把他们的偏导数也排成矩阵?想想我们之前的神经网络的weights矩阵,这是很自然的定义,同时我们需要计算loss对weights矩阵的每一个变量的偏导数,写出这样的形式计算起来比较方便。

那么什么是矩阵对矩阵的梯度呢?我们先看实际神经网络的一个计算情况。对于全连接的神经网络,我们有一个矩阵乘以向量 D=WxD=Wx 【我们这里把向量x看成矩阵】。现在我们需要计算loss对某一个 WijWij 的偏导数,根据我们之前的计算图, WijWij 有多少条出边,那么就有多少个要累加的梯度乘以local梯度。 
假设W是m×n的矩阵,x是n×p的矩阵,则D是m×p的矩阵

026.jpg


根据矩阵乘法的定义

027.jpg


我们可以计算:

028.jpg


请仔细理解上面这一步,如果 k≠i,则不论s是什么,Wks跟Wij不是同一个变量,所以导数就是0;如果k=i,∑sWisxsl=xjl,也就求和的下标s取j的时候有WijWij。 
因此

029.jpg


上面计算了loss对一个Wij的偏导数,如果把它写成矩阵形式就是:

031.jpg


前面我们推导出了对Wij的偏导数的计算公式,下面我们把它写成矩阵乘法的形式并验证【证明】它。

032.jpg


为什么可以写成这样的形式呢?

033.jpg


上面的推导似乎很复杂,但是我们只要能记住就行,记法也很简单——把矩阵都变成最特殊的1 1的矩阵(也就是标量,一个实数)。D=w x,这个导数很容易吧,对w求导就是local gradient x,然后乘以得到dW=dD x;同理dx=dD W。 
但是等等,刚才那个公式里还有矩阵的转置,这个怎么记?这里有一个小技巧,就是矩阵乘法的条件,两个矩阵能相乘他们的大小必须匹配,比如D=Wx,W是m n,x是n p,也就是第二个矩阵的行数等于第一个的列数。 
现在我们已经知道dW是dD”乘以“x了,dW的大小和W一样是m n,而dD和D一样是m p,而x是n p,那么为了得到一个m n的矩阵,唯一的办法就是 dD∗xT 
同理dx是n p,dD是m p,W是m*n,唯一的乘法就是 WT∗dD 
下面是用python代码来演示,numpy的dot就是矩阵乘法,可以用numpy.dot(A,B),也可以直接调用ndarray的dot函数——A.dot(B):
# forward pass
W = np.random.randn(5, 10)
X = np.random.randn(10, 3)
D = W.dot(X)

# now suppose we had the gradient on D from above in the circuit
dD = np.random.randn(*D.shape) # same shape as D
dW = dD.dot(X.T) #.T gives the transpose of the matrix
dX = W.T.dot(dD)
至此,本系列文章的第5部分告一段落。在接下来的文章中,作者将为大家详细讲述关于常见的深度学习框架/工具的使用方法、使用自动求导来实现多层神经网络等内容,敬请期待。 收起阅读 »

环信VP程旭文:从被动客服到主动营销

 [思路网注]程旭文分享了包括国美在线、楚楚街、金融界等很多环信客户在营销领域的应用。怎样做营销投放,再回到针对用户环节的营销、客服怎样使得这个环节可以做到闭环。   【亿邦动力网讯】12月19日消息,在2016亿邦未来零售大会新服务·思路服务分论坛上环信VP...
继续阅读 »
 
[思路网注]程旭文分享了包括国美在线、楚楚街、金融界等很多环信客户在营销领域的应用。怎样做营销投放,再回到针对用户环节的营销、客服怎样使得这个环节可以做到闭环。
   【亿邦动力网讯】12月19日消息,在2016亿邦未来零售大会新服务·思路服务分论坛上环信VP程旭文发表了《从被动客服到主动营销》演讲,他表示:“其实环信在很长时间内我们并没有做营销类的领域,更多的在做SaaS软件,主要做两个软件: 第一,即时通讯云。 第二,全媒体智能客服。”



001.jpg


图为环信VP副总裁程旭文


2016亿邦未来零售大会由亿邦动力网主办,思路网协办,于12月19日-21日在广州白云万达希尔顿酒店举行。国内外电商领域知名企业高管、专家学者、媒体代表共计2000余人出席。

本届大会以“新物种、新规则、新电商”为主题,包括两天的主论坛、五场分论坛、电商经理人之夜以及马蹄社和亿邦疯人会等系列活动。值得关注的是,在本届大会上,电商产业所熟知的如阿里巴巴、京东、唯品会、当当、亚马逊等面孔都没有出现,取而代之的全部是新生代的零售平台和品牌商阵营,反映了电商领域正寻求破局、寻找新增长的行业心态。

(温馨提示:本文为速记初审稿,保证现场嘉宾原意,未经删节,或存纰漏,敬请谅解。)

以下是演讲实录:

程旭文:我也是接到一个命题作文,其实环信在很长时间内我们并没有单独去做营销领域,更多的在做SaaS软件,我们主要做两个软件:

第一,即时通讯云。

第二,全媒体智能客服。

以上两个都是IT领域做在线软件交付。

我们一不小心进入了营销行业,我们可以基于环信的基础,包括服务10万多家APP企业,服务了超过5万家的有客服场景的APP和企业。

我们怎么进入到营销服务领域?在这个过程当中做了什么样的实践?


001.jpg


这张图是我们每一个企业要面对的,你覆盖的客户群体人数、客单价多少,可以算出整个生意有多大。这个沉淀用户是指已经触达到用户,或者注册了网站,或者订阅了你的微信公众号等等这些用户。还有一部分是你的付费用户,对于电商企业来讲很重要的两个环节是怎样降低你的获取成本,怎样降低用户转化成本,我们做了很多的实践,我们接触了很多客户,我们发现在这个环节可以给用户带来很多价值。


002.jpg


如果拿一张图把整个营销获取用户到转化客户变成付费用户的过程当中,上面部分是获取成本,下面两个图是关乎转化率。用户的角度看上去接触的点是营销环节和客服环节,进而往后走就是一些落地点,现在大部分电商说落地点放在淘系,很多电商说有自己的网站、APP,这些都是企业商品的落地点,再往后走就是企业客户关系系统和物流、企业资源管理、ERP等等。

今天王詠谈了关于企业大数据、关于电商大数据方面的话题,其实我感触特别深刻,用户很多行为,包括购买前的数据、购买后的数据,或多或少在企业内都有管理系统。通过这一套系统怎样做营销投放,再回到针对用户环节的营销、客服怎样使得这个环节可以做到闭环。

环信在过去的实践当中我们会基于一个核心的点,就是讲“长连接”技术,指你时时刻刻和你的潜在客户、和你的目标客户、付费客户保持一条长连接通道,使得你随时可以找到你的客户,可以随时触达他们。

从三个维度讲:

第一,长连接相对传统客服可以随时触达你的客户,改变被动的接受用户询问的现状。

第二,相对于传统的消息推送,长连接使得你与客户的互动是双向的、及时的。

第三,结合自己的数据管理平台、环信的管理平台,可以使得你精准的挑选客户。

举几个例子:

第一,国美,国美有上亿的客户,他们客服系统用的是我们的,营销环节有什么机制可以随时找到他们、随时联系他们,随时和他们互动,他们的反馈可以让我们知道。

比如说用户,国美APP放在后台或者锁屏状态,我们可以做到哪怕这个后台或者锁屏状态客服的坐席依然根据一定的规则发放消息给国美所有客户和部分客户,使得我们消息可以及时的触达,用户一点击基本上上做到客服状态了,后台可以监测哪些用户点了这个消息,有多少点了消息,有多少回复消息,可以做精准统计,用户行为洞察可以比单向消息推送更加细致。

第二,楚楚街,他们号称“小淘宝”定向推动2000套儿童座椅给3-4岁儿童的妈妈,我们给楚楚街做了客户管理系统,通过这些找到目标群体,通过基于长连接可以时时刻刻和用户保持长连接的客服系统,将一些非常具有价值的营销消息给APP用户,只要APP没有被卸载,用户就可以看到我们消息,及时产生互动。

第三,在金融界,互联网金融在大的范畴类也属于在线卖东西,只不过交付形式略有不同。

一是为电话销售配置一个APP的主动营销的平台,当用户上线及时的推送,用户一上APP及时的推送用户行为,之前的历史行为、购买习惯给客户经理。

二是结合用户分析推荐相关产品和用户密切互动。一些新的客户可以进入销售公海池,让新的销售抢单,立刻和客户产生互动。

再举一个例子,楚楚街利用“回呼”功能降低退换货率,我们有的时候发现这个客户下单了,但是有点问题,比如说地址不对等等,我们审核的时候立刻发现这个单子有问题,通过“回呼”找到客户,减少因为小事情碰到一些退货情况。

综合来讲,我们之前并没有主动做这方面,但是我们发现通过两个产品结合已经做了很多精准化营销方面的东西,我们与客户保持了长连接,这个长连接是即时通讯云的产品。在淘系、在腾讯,我们都离不开他们,因为他们与客户保持了密切联系,你们依然可以构建自己的APP,通过环信的即时通讯与用户保持长连接,业界当中除了旺旺、除了微信、除了陌陌第四大和用户保持长连接的平台就是环信。

我的演讲到此结束,如果大家需要交流可以来我们展台继续沟通。谢谢大家! 收起阅读 »

游戏测试与软件测试的区别!

关于软件测试与游戏测试的区别,网上也有几篇文章提到,但是感觉没有描述的特别清晰,原因无非2点:一是即做过软件测试又做过游戏测试的人本身不多,二是在软件和游戏测试都做过的这一小撮人里善于归纳总结的更是少之又少。笔者不才,恰恰软件测试和游戏测试都做过,而且都从事的...
继续阅读 »
关于软件测试与游戏测试的区别,网上也有几篇文章提到,但是感觉没有描述的特别清晰,原因无非2点:一是即做过软件测试又做过游戏测试的人本身不多,二是在软件和游戏测试都做过的这一小撮人里善于归纳总结的更是少之又少。笔者不才,恰恰软件测试和游戏测试都做过,而且都从事的时间较长,感触颇多,今天尝试阐述下2者的不同,希望能够抛砖引玉,共同探讨学习。

 

游戏本质也是软件的一种,所以从测试工程的角度来讲,游戏测试与软件测试的本质是完全相同的。2者的不同更多的是在表象层面或者流程方面,我们可以把游戏测试看作软件测试的子类,它继承了软件测试这个父类的特性,又有自己的一些新特性。

 

笔者通过归纳总结,把游戏测试相对软件测试的不同归纳为以下几点:

1. UI&&UE

2. 数值

3. 活动

4. 进度

5. 工具

6. 性能

7. 安全

8. 合服(针对网游)

9. 交互

10. 网络

 

下面我们就每一点来详细探讨下。

1、UI&&UE。相对来讲UI&&UE在游戏和软件测试中,重要性并非很高,但它们确是用户和测试人员最直观感受的部分,也最受“非专业人士”的关注,游戏行业尤甚。对大部分软件来说,UI&&UE的重要性没有游戏那么高,毕竟软件使用过程愉悦感和趣味性并非是重要的事情,我们日常使用各种各样的软件时肯定深有体会,大部分情况是用软件来完成一项任务,能完成就好了,在使用过程中很难体会到上面说的愉悦感和趣味性。而游戏则不然,在玩游戏的过程中,愉悦感和趣味性是至关重要的,如果缺失了这些要素,用户可能瞬间就流失了,也就意味着这款游戏失败了。这好比高层小户型和海景别墅,虽然都能满足居住需求,但给人的感觉是完全不同的。

 

2、数值。数值对游戏而言是至关重要的,无论是单机游戏还是网络游戏,玩家非常重视自己角色的数值增长,任何差错都可能导致用户的抱怨甚至流失。另一个层面是游戏的功能之间的耦合度非常高,数值之间有着千丝万缕的关联。所以测试的过程中需要关注每个数值变化带来的各种影响。而软件功能之间的耦合度则没有这么高,很多情况下功能之间的数值是相对独立的。而且软件的用户很多时候并不关注内部的数值,能完成所需即可,细微的差错甚至都没人关心。举个例子,比如很多显示开机速度的软件,在用户打开电脑时会提示用户开机速度击败了百分之多少的其它用户,至于是20%还是25%,可能对用户而言没什么太大的差别。而游戏则不然,比如一个角色的战斗力是1000,下次登陆变成999,仅仅是1的差距,玩家可能就会愤怒的打客服电话质问了。

 

3、活动。很多软件也经常搞活动,笔者经常遇到某邮箱或某论坛搞活动送积分之类的,但是在游戏中,活动则是频度更高的一种玩法。所以测试过程中可能受到的关注度更高一些,尤其是网络游戏。游戏活动的测试更关注时间与资源产出,如开启时间,关闭时间,资源产出概率等。因为一个活动的开启和关闭及产出都已经提前公告给玩家,如果出了任何差错,都会导致玩家不满。而且一个活动完毕后可能紧接另一个活动,任何差错都可能导致更大的损失。而软件上的活动则没这么严格的概念。

 

4、进度。在软件开发和测试过程中,延期是非常普遍的情况。很多软件测试人员的时间观念也没那么强。游戏则是非常不同的,由于游戏的**倾向,所以其产业链涉及很多前期的市场推广,各种广告和推广活动都是真金白银砸下去的,任何延期可能都会导致前期的推广功亏一篑及商业上的信誉,这些损失都是不可接受的。所以游戏测试作为产品发布前的最后一环,必须严格控制版本进度,确保能够按期交付。

 

5、工具。游戏测试依赖更多的测试工具,因为用户的数值和角色状态千差万别,为了尽量模拟用户状态,测试过程中总需要造出各色各样的测试数据,而制造这些数据,则需要测试工具的帮助。另一个层面是游戏测试还需要对测试工具本身的正确性进行测试,确保工具本身是正确的。这点在传统软件测试行业则是不多见的。

 

6、性能。性能测试对游戏而言也是至关重要的一点,无论在台式机还是移动设备上,任何游戏的卡顿都会让玩家产生厌恶感。游戏测试过程中比较重视的是客户端的内存和cpu的使用率,确保游戏能够流畅的运行。对网络游戏而言,服务端的性能也十分重要,一款良好的网游,需要服务器能够稳定持久的运行。而且我们也希望大部分用户都能玩我们的游戏,而用户的设备则差异性很大,尤其是移动设备。所以我们必须确保客户端的性能符合我们的预期标准,以使更多的玩家能够玩我们的游戏。软件则没太多这方面的需求。

 

7、安全。安全对软件和游戏而言都十分重要。但是对游戏而言,则是关乎身家性命的事情,很多游戏都死于外挂横行。而且游戏的客户端与服务端的交互非常频繁,数据安全更加凸显。所以测试的时候更加关注安全方面的测试。有资源产出的地方则有安全测试的地方。防刷防外挂,是游戏测试人员始终要保持谨慎认真的对待的地方。

 

8、合服。这个可能是游戏的独有特色。有时候服务器中用户便少,为了带给玩家更好的游戏体验,需要合并几组服务器为1组。在合服的过程中需要保证原有服务器和目标服务器中所有用户的数据信息不发生错乱。涉及到用户方方面面的数据信息,复杂度也比较高,所以也许要测试人员认真的测试。确保测试无误后,才能正式开始合服操作。

 

9、交互。更多的时候是相对网络游戏而言,网游中很大程度的乐趣都来源于玩家与玩家之间的交互。这一特性在传统软件(此处请忽略各种社交软件)中并不多见。玩家交互的越频繁,则意味着数据之间交互的程度越高,数据之间的复杂变换及相互影响需要我们时刻关注。

 

10、网络。网络对于网络游戏是必不可少的,游戏的实时交互性比较高,游戏过程中突然断网的痛苦是难以忍受的。所以对网络的测试要求也比较高,因为不同用户用的网络运营商可能不同,不同地区的网络信号也不同,甚至移动过程中会出现不同网络之间的切换,这些都是需要我们去认真测试的。这样才能尽量保证不同网络条件下用户的体验达到最佳。

想要高效的完成app功能测试,就需要选择一款合适的功能测试工具。尽管现阶段存在少数不采用任何功能测试工具,从事功能测试外包项目的软件服务企业。短期来看,这类企业盈利状况尚可,但长久来看,它们极有可能被自动化程度较高的软件服务企业取代。

TestBird  - 手游和App自动化测试平台 收起阅读 »

环信移动客服v5.5.1更新:新增客户资料自定义

客服模式 支持查看客服同事的真实姓名 客服与同事在移动客服系统交流时,可以查看对方的真实姓名,更利于同事间沟通。支持以下两种场景:   与同事聊天时,将鼠标放在“客服同事”列表中同事的昵称上,可以查看该同事的真实姓名; 转接会话时,将鼠标放在...
继续阅读 »
客服模式

支持查看客服同事的真实姓名

客服与同事在移动客服系统交流时,可以查看对方的真实姓名,更利于同事间沟通。支持以下两种场景:
 

  • 与同事聊天时,将鼠标放在“客服同事”列表中同事的昵称上,可以查看该同事的真实姓名;

  • 转接会话时,将鼠标放在“转接会话”对话框中同事的昵称上,可以查看该同事的真实姓名。



注意:客服可以在客服模式下“客服信息”页面设置自己的名字(真实姓名);管理员可以在管理员模式下“成员管理 > 客服”页面设置其他客服的真实姓名。

支持查看待接入会话详情

在待接入页面,点击任意一条会话,可以查看该会话的消息详情。

前提条件:管理员进入“管理员模式 > 设置 > 系统开关”页面,打开“客服查看待接入详情”开关。 

001.png

租户下待接入会话数上限

新增待接入会话数上限,每个租户允许的最大待接入会话数为1000,如果某个租户下坐席数超过5个,则该租户的最大待接入会话数为坐席数x200。

待接入会话数超过上限后,不允许访客创建新的会话,当访客试图接入时提示,系统繁忙无法接入。

为避免访客无法接入的情况,当租户的待接入会话数即将达到上限时,系统向消息中心发送通知提醒管理员;当租户的待接入会话数已达到上限时,系统会再次向消息中心发送通知提醒管理员及时处理。

支持客服主动发起会话

在待接入页面,客服可以查看正在访问网站的访客列表,并主动发起会话。发起会话后,会话进入客服的进行中会话列表,客服可以主动与访客聊天。

该功能为增值服务,如需开通,请联系环信商务经理。开通后,在网页访客端进行配置eventCollector为true即可使用。关于详细配置方法,请参考网页渠道集成。

呼叫中心支持电话转接和呼叫保持

呼叫中心支持电话转接和呼叫等待功能。在通话过程中,如果电话需要转接,可以点击转接按钮 [转接] ,将电话转接给呼叫中心客服同事;如果有其他操作处理,需要暂停通话,可以点击保持按钮 [保持] ,将通话置为“保持中”状态,完成操作后,可以手动恢复通话。

呼叫中心功能为增值服务,如需开通,请联系环信商务经理。

002.png

管理员模式

优化机器人开关设置

优化机器人开关的“工作时间设置”,支持为机器人设置不同的工作场景:
 

  • 全天接会话:访客发起会话时,由机器人接待。

  • 上班时间客服全忙以及下班时间接会话:在上班时间,访客发起会话时,如果客服全忙,会话由机器人接待;在下班时间,访客发起会话时,由机器人接待。

  • 仅下班时间接会话:在下班时间,访客发起会话时,由机器人接待。



机器人回答不了时,访客可以选择转人工客服。转人工后,如果有空闲客服则自动调度,如果没有空闲客服,则会话进入待接入,客服可以手动接入会话。

注意:该版本更新前的“仅下班时间接会话”与更新后的“上班时间客服全忙以及下班时间接会话”功能一致。如果您之前选择了“仅下班时间接会话”,更新后默认选择的是“上班时间客服全忙以及下班时间接会话”,您可以根据您的需要调整机器人开关。

003.png

优化上下班时间设置

“设置 > 系统开关”页面的“上下班时间”设置更名为“工作时间设置”,“会话结束语”和“下班提示语”上移至“工作时间设置”之前,原有数据保持不变。“工作时间设置”支持为周一至周日设置单独的上下班时间,以适应不同的工作时间场景:
 

  • 为工作日(周一至周五)和周末(周六、周日)设置不同的上下班时间段(如下图)

  • 为工作日的上午、下午设置不同的上下班时间段

  • 以星期为周期,自定义每天的上下班时间



进入“设置 > 系统开关”页面,在“工作时间设置”区域,点击“添加新的工作时间”,设置新的工作时间段。

004.png

历史会话支持分配功能

管理员可以将历史会话重新分配给客服或技能组,分配后,生成一个新的会话。
 

  • 分配给客服时,新会话直接进入客服的进行中会话列表。

  • 分配给技能组时,如果技能组内有空闲客服,新会话进入空闲客服的进行中会话列表;如果技能组内客服全忙,新会话进入该技能组的待接入会话列表。



进入管理员模式,选择“历史会话”,点击会话右侧的转接按钮可以对该会话进行分配。

005.png

问候语中增加访客昵称

新增在问候语中增加访客昵称的功能,提升亲密度。问候语包含企业问候语、客服问候语、技能组问候语,在这三种问候语中均可以设置。设置方式为,在问候语中添加特殊字符和默认称呼(##亲##)。当访客昵称有效时,显示访客昵称;当访客昵称无效时,显示默认称呼(亲)。默认称呼可以自定义。

例如,设置企业问候语为“##亲##,您好,很高兴为您服务!”
 

  • 当访客昵称有效时(访客昵称和ID不一致),假设访客昵称为Jon,该访客收到的问候语为“Jon,您好,很高兴为您服务!”

  • 当访客昵称无效时(访客昵称和ID一致),访客收到的问候语为“亲,您好,很高兴为您服务!”



客户资料自定义

新增“客户资料自定义”功能,允许管理员设置在系统中显示哪些客户资料,包括系统字段和自定义字段,并对这些字段进行排序。设置后,新的字段列表和顺序将显示在客服模式下“会话”、“历史会话”和“客户中心”等页面的“资料”页签,以及管理员模式下“客户中心”、“历史会话”、“当前会话”等页面的“资料”页签。

进入“设置 > 客户资料自定义”页面对客户资料进行自定义,步骤如下:

1. 添加自定义字段。点击“添加自定义字段”按钮,在对话框中输入字段名称,选择字段格式,并进行相应设置,点击“保存”按钮。重复该步骤,可添加多个自定义字段。

自定义字段默认对坐席可见,当关闭“坐席可见”开关时,在客服模式下不显示该字段。

006.png

2. 设置字段是否显示,以及在“资料”页签的排列顺序。在“字段开关”一列,勾选 [勾选] 需要显示的字段,取消勾选 [取消勾选] 不需要显示的字段。点击字段后面的排序按钮 [排序] ,可将该字段的顺序上移一位。

示例,根据下图的设置,“资料”页签将只显示:昵称、名字、ID、微信号、微博账号、描述。

007.png

允许客服查看待接入会话详情

新增“客服查看待接入详情”功能,在客服模式的待接入页面,点击任意一条会话,可以查看该会话的历史消息。进入“设置 > 系统开关”页面,打开“客服查看待接入详情”开关。该开关默认关闭。

008.png

待接入超时提醒

新增“待接入超时提醒”功能,当访客进入待接入排队超过一定时间后,系统将自动发送消息提示访客。进入“设置 > 系统开关”页面,打开“待接入超时提醒”开关,并设置超时提示语、排队超时提醒时间及提醒次数。该开关默认关闭。
 

  • 当提醒次数设置为1次时,访客在待接入排队时长达到“排队超时提醒时间”时,系统发送“超时提示语”给访客;

  • 当提醒次数设置为多次时(例如3次),访客在待接入排队时长达到“排队超时提醒时间”时,系统发送“超时提示语”给访客,之后,当每次达到“排队提醒间隔”设定的时长,系统再次发送“超时提示语”给访客,直到会话被客服接起,或达到“提醒次数”。




009.png

待接入超时结束会话

新增“待接入超时结束会话”功能,当访客排队时长达到设定数值时,仍然没有被客服接入,会话将被自动结束。进入“设置 > 系统开关”页面,打开“待接入超时结束会话”开关,并设置超时提示语、超时时间及会话标签和备注。该开关默认关闭。

010.png

支持转人工指定技能组

支持为机器人设置转人工指定技能组。为了不影响现有会话路由规则,默认情况下不指定。

如果没有开通多机器人功能,分两种场景:
 

  • 场景一:使用默认配置(不指定)。会话经过默认机器人转接人工客服时,按照原有会话路由规则分配给对应的技能组或客服。

  • 场景二:设置转人工指定某个技能组。会话经过默认机器人转接人工客服时,都转给指定的技能组。



如果开通了多机器人功能,建议配置如下:
 

  • 为每个新创建的机器人指定不同的技能组。这样,会话经过机器人转接人工客服时,将分配给指定的技能组,从而实现机器人绑定技能组功能。



进入“智能机器人 > 机器人设置”页面,选择一个机器人,再次选择“自动回复 > 转人工设置”页签,为该机器人选择“转人工指定技能组”。

011.png

机器人问答优化功能

新增机器人问答优化功能,系统自动收集机器人未能匹配的重复出现的访客消息,并以列表的形式显示。您可以将这些访客消息添加到知识规则中,并设置对应的答案,提高机器人回答的匹配率和准确性。

进入“智能机器人 > 机器人设置 > 问答优化”页面,点击任意一条未匹配问句后的加号(+),可以将该问句添加到知识规则,支持三种方式:
 

  • 添加到推荐知识规则:如果存在相似度高的知识规则,系统会将其展示为“推荐知识规则”,选择该知识规则,并点击“添加”按钮即可;

  • 将问句添加到现有知识规则:选择“将问句添加到现有知识规则”,系统展示现有知识规则列表,您可以选择并添加该问句到任一知识规则;

  • 创建新知识规则并添加此问句:选择“创建新知识规则并添加此问句”,系统自动为该问句创建一条知识规则,请您手动为该问句添加对应答案(如果知识规则中没有答案,机器人会回复空消息)。



机器人问答优化功能为增值服务,如需开通,请联系环信商务经理。

012.png


新增“删除坐席”事件

自定义事件推送功能新增“删除坐席”事件,当坐席被删除时,可将相关信息以回调方式自动推送到其他系统。进入“设置 > 自定义事件推送”页面,点击“创建事件推送”,填写自定义事件名称、接收事件的服务器地址,勾选需要推送的事件,并保存。

自定义事件推送功能为增值服务,如需开通,请联系环信商务经理。

013.png


客户之声支持手动添加关注的关键词

优化客户之声的“热门关键词”,支持查看一段时间内的热门关键词,并且支持手动添加您关注的业务相关的关键词。

进入客户之声页面,在“热门关键词”区域的右上角可以选择时间范围;点击“关注词设置”,可以添加您关注的关键词,更新的关注词将于第二天生效,并在词云中高亮显示。

客户之声功能为增值服务,如需开通,请联系环信商务经理。

014.png


 
环信移动客服更新日志http://docs.easemob.com/cs/releasenote/5.6 
 
环信移动客服登陆地址http://kefu.easemob.com/
收起阅读 »

没想到你是这样的环信!!!

一封来自环信小伙伴的来信,没想到你是这样任劳任怨,兢兢业业,恪尽职守的环信!   致环信:    在自如客服项目的支持过程中,贵公司的孔令莹兢兢业业、不辞劳苦的工作态度令人由衷赞佩,必须提笔赞扬一下,为工作上能有这样值得信赖的合作伙伴感到欣慰和放心。   ...
继续阅读 »

640.jpg


一封来自环信小伙伴的来信,没想到你是这样任劳任怨,兢兢业业,恪尽职守的环信!
 
致环信:

   在自如客服项目的支持过程中,贵公司的孔令莹兢兢业业、不辞劳苦的工作态度令人由衷赞佩,必须提笔赞扬一下,为工作上能有这样值得信赖的合作伙伴感到欣慰和放心。

   我们客服系统是早9:00-22:00在线提供支持,自如客是O2O的业务应用模式,我们的客服需要及时解决来自租客和业主的各种问题,涉及多个渠道接入:4个微信号,1个APP,1个门户。每个渠道的接入情况都不一样,关联系统复杂,在这样的业务背景下,很考验环信客服系统无停歇稳定运行能力,以及项目支持人员的应急处理能力。我们要特别感谢孔令莹,从支持自如客服系统以来,不论是工作日还是节假日,我们任何时候提出的问题,她都会快速响应帮我们排查协调。令人印象深刻的是,有很多次都是由于我们内部系统原因造成,但孔令莹依旧会和我们一起帮助客服同学定位原因,直到客服系统正常恢复使用。还记得在国庆节和双十一期间,我问到环信是否会安排轮班值守人员时,孔令莹的答复是遇到问题随时联系她,会及时为我们解决问题。这样的态度让我们倍感欣慰,她用行动诠释了她的承诺,这样的工作精神令我们感动。

   为她以客户为先的工作精神而点赞,希望贵司能对这样优秀的员工给予表扬,特发此邮件表达诚挚的谢意。

   自如服务产品部

   2016-11-30
  收起阅读 »

比较简单的解决android 3.2.2 昵称和头像的问题

我这个方法我自己正在使用,能比较简单的解决 第一步:本地新建一张环信用户表,字段有id(app本地的userId)、昵称、头像url,至于用什么数据库自己决定;第二步:写上环信用户表的query、insert和update操作;第三步:在EMClient.ge...
继续阅读 »
我这个方法我自己正在使用,能比较简单的解决
  • 第一步:本地新建一张环信用户表,字段有id(app本地的userId)、昵称、头像url,至于用什么数据库自己决定;
  • 第二步:写上环信用户表的query、insert和update操作;
  • 第三步:在EMClient.getInstance().login()的onSuccess()方法内使用insert插入自己的userId、昵称和头像url数据到数据表去,当然,如果已经插入过就update,要始终保持数据是最新的,因为你自己的可能会改变昵称和头像;
  • 第四步:在进入环信的 ChatActivity.class 之前跟第三步一样保存你要聊天的对象的userId、昵称和头像url,同样的也是每次都要保存一下,防止聊天对象的昵称和头像不是最新的;
  • 第五步:如果基于官方demo的 在DemoHeler.class的getUseriInfo()方法里改为如下:

private EaseUser getUserInfo(String username) {
EaseUser user = null;
UserInfoForEM userInfoForEM = DBUtil.findUserInfoForEM(username);
if (null != userInfoForEM) {
user = new EaseUser(username);
user.setAvatar(userInfoForEM.getUserHeadPhoto());
user.setNick(userInfoForEM.getNickName());
}
return user;
}

如果没有DemoHeler类,那么就在下面这个方法处理:
EaseUI.getInstance().setUserProfileProvider(new   EaseUI.EaseUserProfileProvider() {
@Override
public EaseUser getUser(String username) {
EaseUser user = null;
UserInfoForEM userInfoForEM = DBUtil.findUserInfoForEM(username);
if (null != userInfoForEM) {
user = new EaseUser(username);
user.setAvatar(userInfoForEM.getUserHeadPhoto());
user.setNick(userInfoForEM.getNickName());
}
return user;
}
});

解释一下,UserInfoForEM 是我自己的环信用户表实体类,如果userInfoForEM=null,聊天昵称就会显示注册环信的id。

好了,完了,是不是很简单,根本不要考虑什么服务器获取,也不用考虑什么附加字段,也不会去考虑非得收到一条信息才能显示。要说明一下我这是基于官方SDK3.2.2版本以及官方demo基础上弄的, 希望对大家有帮助! 收起阅读 »

B2B服务年度创新论坛暨“互联网+”时代的企业服务黑科技

活动背景 企业如何在疲软经济中,抓住价值的突破口?如何在资本寒冬下,压缩管理成本?都已成为大家必须直面的攻坚重点。 此次论坛中,2B企业服务领域的优秀领导人,将逐一登场,为广大企业管理者与新晋创业者解读企业高效率、低成本运行的“黑科技”。既然大环境...
继续阅读 »


912535552587567_v2.jpg


活动背景

企业如何在疲软经济中,抓住价值的突破口?如何在资本寒冬下,压缩管理成本?都已成为大家必须直面的攻坚重点。

此次论坛中,2B企业服务领域的优秀领导人,将逐一登场,为广大企业管理者与新晋创业者解读企业高效率、低成本运行的“黑科技”。既然大环境无法改变,企业必须快速掌握逆境生存策略,在企业商旅、财务管理、财税报销、电子签约、APP推广等各方面从传统方式转至互联网新模式。

诚邀广大创业者与管理者一起来现场听听诸位创始人的企业管理之道,学学2B大咖的价值再生之法。

经济寒冬之下,我们更当“傲雪”而立,12月22日,不见不散。

30402538711705046.jpg


我要报名 收起阅读 »

APP自动化测试框架

目前常见的APP自动化测试框架有以下几类:   1. Instrumentation Instrumentation,早期Google提供的Android自动化测试工具类。它和Activity有点类似,但其没有界面,通过将主程序和测试程序运行在同一个进程...
继续阅读 »
目前常见的APP自动化测试框架有以下几类:

 

1. Instrumentation

Instrumentation,早期Google提供的Android自动化测试工具类。它和Activity有点类似,但其没有界面,通过将主程序和测试程序运行在同一个进程中,在程序运行期间,模拟按键按下、抬起、屏幕点击、滚动、屏幕长按等事件,监控主程序的工具类。缺点是受到Android进程安全限制不可跨App,对测试人员的编程能力要求较高,需要对AndroidManifest.xml文件进行配置。

 

2. Uiautomator

Uiautomator,也是Android提供的自动化测试框架,基本上支持所有的Android事件操作。与Instrumentation不同的是,测试代码和被测应用程序分别运行在不同的进程内,相互独立,可以跨多个App。缺点是不支持WebView,不支持获取toast文本,只适用于SDK level 16(Android 4.1)及以上。

 

3. Robotium

基于Instrumentation实现,提供的接口可以满足大部分自动化需求,使用方法简单,支持Activities、Dialogs、Toasts、Menus、Context Menus和其他Android SDK控件。缺点是测试人员需了解Android组件相关知识,同样不可跨App。

 

4. Monkey

Monkey是Android中的一个命令行工具,可以运行在模拟器里或实际设备中。它向系统发送伪随机的用户事件流(如按键输入、触摸屏输入、手势输入等),实现对正在开发的应用程序进行压力测试。Monkey测试是一种为了测试软件的稳定性、健壮性的快速有效的方法。

 

5. MonkeyRunner

Monkeyrunner工具提供了一个API,使用此API写出的程序可以在Android代码之外控制Android设备和模拟器。Monkeyrunner工具的主要设计目的是用于测试功能/框架水平上的应用程序和设备,或用于运行单元测试套件。

 

6. MonkeyTalk

MonkeyTalk是Gorilla Logic的一款开源的支持录制回放并跨平台的自动化工具。支持iOS 和 Android,它可以为应用进行真实的,功能性交互测试。它提供简单的 “smoke tests”,复杂数据驱动的测试套件。MonkeyTalk 支持原生,移动和混合应用,真实设备或者模拟器。MonkeyTalk 使得场景捕获非常容易,可以记录高级别,可读的测试脚本。可以真实测试用户行为,用户交互如触摸、手指滚动、长按等,还支持HTML5的一些特性,比如本地存储、session存储、应用缓存等。缺点是需要应用源码。

 

7. Appium

Appium是最近比较热门的框架,支持IOS、Android和FirefoxOS平台的UI测试,支持WebDriver兼容的任何语言编写测试脚本,Android SDK Level在16及以上时,底层使用的UIAutomator,低于16使用Selendroid。

自动化框架种类多多,在选取框架时,除了需要适合做UI测试外,还需要具备以下几点特性:工具开源,易于扩展; 脚本编写简洁,维护成本低;满足Android客户端的自动化需求;便与校验结果的正确性;可用于持续集成。

 

 

想要高效的完成app功能测试,就需要选择一款合适的功能测试工具。尽管现阶段存在少数不采用任何功能测试工具,从事功能测试外包项目的软件服务企业。短期来看,这类企业盈利状况尚可,但长久来看,它们极有可能被自动化程度较高的软件服务企业取代。

TestBird  - 手游和App自动化测试平台 收起阅读 »

李理:自动梯度求解 反向传播算法的另外一种视角

本系列文章面向深度学习研发者,希望通过Image Caption Generation,一个有意思的具体任务,深入浅出地介绍深度学习的知识。本系列文章涉及到很多深度学习流行的模型,如CNN,RNN/LSTM,Attention等。本文为第四篇。 作者:李理 目...
继续阅读 »
本系列文章面向深度学习研发者,希望通过Image Caption Generation,一个有意思的具体任务,深入浅出地介绍深度学习的知识。本系列文章涉及到很多深度学习流行的模型,如CNN,RNN/LSTM,Attention等。本文为第四篇。

作者:李理 
目前就职于环信,即时通讯云平台和全媒体智能客服平台,在环信从事智能客服和智能机器人相关工作,致力于用深度学习来提高智能机器人的性能。

相关文章: 
环信李理:从Image Caption Generation了解深度学习
李理:从Image Caption Generation理解深度学习(part II)
李理:从Image Caption Generation理解深度学习(part III)
接下来介绍一种非常重要的神经网络——卷积神经网络。这种神经网络在计算机视觉领域取得了重大的成功,而且在自然语言处理等其它领域也有很好的应用。深度学习受到大家的关注很大一个原因就是Alex等人实现的AlexNet(一种深度卷积神经网络)在LSVRC-2010 ImageNet这个比赛中取得了非常好的成绩。此后,卷积神经网络及其变种被广泛应用于各种图像相关任务。

这里主要参考了Neural Networks and Deep Learning和cs231n的课程来介绍CNN,两部分都会有理论和代码。前者会用theano来实现,而后者会使用我们前一部分介绍的自动梯度来实现。下面首先介绍Michael Nielsen的部分(其实主要是翻译,然后加一些我自己的理解)。

前面的话

如果读者自己尝试了上一部分的代码,调过3层和5层全连接的神经网络的参数,我们会发现神经网络的层数越多,参数(超参数)就越难调。但是如果参数调得好,深的网络的效果确实比较浅的好(这也是为什么我们要搞深度学习的原因)。所以深度学习有这样的说法:“三个 bound 不如一个 heuristic,三个 heuristic 不如一个trick”。以前搞机器学习就是feature engineering加调参,现在就剩下调参了。网络的结构,参数的初始化,learning_rate,迭代次数等等都会影响最终的结果。有兴趣的同学可以看看Michael Nielsen这个电子书的相应章节,cs231n的Github资源也有介绍,另外《Neural Networks: Tricks of the Trade》这本书,看名字就知道讲啥的了吧。

不过我们还是回到正题“卷积神经网络”吧。

CNN简介

在之前的章节我们使用了神经网络来解决手写数字识别(MNIST)的问题。我们使用了全连接的神经网络,也就是前一层的每一个神经元都会连接到后一层的每一个神经元,如果前一层有m个节点,后一层有n个,那么总共有m*n条边(连接)。连接方式如下图所示:

001.jpg


具体来讲,对于输入图片的每一个像素,我们把它的灰度值作为对应神经元的输入。对于28×28的图像来说,我们的网络有784个输入神经元。然后我们训练这个网络的weights和biases来使得它可以正确的预测对应的数字。

我们之前设计的神经网络工作的很好:在MNIST手写识别数据集上我们得到了超过98%的准确率。但是仔细想一想的话,使用全连接的网络来识别图像有一些奇怪。因为这样的网络结构没有考虑图像的空间结构。比如,它对于空间上很近或者很远的像素一样的对待。这些空间的概念【比如7字会出现某些像素在某个水平方向同时灰度值差不多,也就是上面的那一横】必须靠网络从训练数据中推测出来【但是如果训练数据不够而且图像没有做居中等归一化的话,如果训练数据的7的一横都出现在图像靠左的地方,而测试数据把7写到右下角,那么网络很可能学不到这样的特征】。那为什么我们不能设计一直网络结构考虑这些空间结构呢?这样的想法就是下面我们要讨论的CNN的思想。

这种神经网络利用了空间结构,因此非常适合用来做图片分类。这种结构训练也非常的快,因此也可以训练更“深”的网络。目前,图像识别大都使用深层的卷积神经网络及其变种。

卷积神经网络有3个基本的idea:局部感知域(Local Recpetive Field),权值共享和池化(Pooling)。下面我们来一个一个的介绍它们。

局部感知域

在前面图示的全连接的层里,输入是被描述成一列神经元。而在卷积网络里,我们把输入看成28×28方格的二维神经元,它的每一个神经元对应于图片在这个像素点的强度(灰度值),如下图所示:

002.jpg


和往常一样,我们把输入像素连接到隐藏层的神经元。但是我们这里不再把输入的每一个像素都连接到隐藏层的每一个神经元。与之不同,我们把很小的相临近的区域内的输入连接在一起。

更加具体的来讲,隐藏层的每一个神经元都会与输入层一个很小的区域(比如一个5×5的区域,也就是25个像素点)相连接。隐藏对于隐藏层的某一个神经元,连接如下图所示:

003.jpg


输入图像的这个区域叫做那个隐藏层神经元的局部感知域。这是输入像素的一个小窗口。每个连接都有一个可以学习的权重,此外还有一个bias。你可以把那个神经元想象成用来分析这个局部感知域的。

我们然后在整个输入图像上滑动这个局部感知域。对于每一个局部感知域,都有一个隐藏层的神经元与之对应。为了具体一点的展示,我们首先从最左上角的局部感知域开始:

004.jpg


然后我们向右滑动这个局部感知域:

005.jpg


以此类推,我们可以构建出第一个隐藏层。注意,如果我们的输入是28×28,并且使用5×5的局部关注域,那么隐藏层是24×24。因为我们只能向右和向下移动23个像素,再往下移动就会移出图像的边界了。【说明,后面我们会介绍padding和striding,从而让图像在经过这样一次卷积处理后尺寸可以不变小】

这里我们展示了一次向右/下移动一个像素。事实上,我们也可以使用一次移动不止一个像素【这个移动的值叫stride】。比如,我们可以一次向右/下移动两个像素。在这篇文章里,我们只使用stride为1来实验,但是请读者知道其他人可能会用不同的stride值。

共享权值

之前提到过每一个隐藏层的神经元有一个5×5的权值。这24×24个隐藏层对应的权值是相同的。也就是说,对于隐藏层的第j,k个神经元,输出如下:


σ(b+∑l=04∑m=04wl,maj+l,k+m)


这里,σ是激活函数,可以是我们之前提到的sigmoid函数。b是共享的bias,Wl,m 是5×5的共享权值。ax,y 是输入在x,y的激活。

【从这个公式可以看出,权值是5×5的矩阵,不同的局部感知域使用这一个参数矩阵和bias】

这意味着这一个隐藏层的所有神经元都是检测同一个特征,只不过它们位于图片的不同位置而已。比如这组weights和bias是某个局部感知域学到的用来识别一个垂直的边。那么预测的时候不管这条边在哪个位置,它都会被某个对于的局部感知域检测到。更抽象一点,卷积网络能很好的适应图片的位置变化:把图片中的猫稍微移动一下位置,它仍然知道这是一只猫。

因为这个原因,我们有时把输入层到隐藏层的映射叫做特征映射(feature map)。我们把定义特征映射的权重叫做共享的权重(shared weights),bias叫做共享的bias(shared bais)。这组weights和bias定义了一个kernel或者filter。

上面描述的网络结构只能检测一种局部的特征。为了识别图片,我们需要更多的特征映射。隐藏一个完整的卷积神经网络会有很多不同的特征映射:

006.jpg


在上面的例子里,我们有3个特征映射。每个映射由一个5×5的weights和一个biase确定。因此这个网络能检测3种特征,不管这3个特征出现在图像的那个局部感知域里。

为了简化,上面之展示了3个特征映射。在实际使用的卷积神经网络中我们会使用非常多的特征映射。早期的一个卷积神经网络——LeNet-5,使用了6个特征映射,每一个都是5×5的局部感知域,来识别MNIST数字。因此上面的例子和LeNet-5很接近。后面我们开发的卷积层将使用20和40个特征映射。下面我们先看看模型学习到的一些特征:

007.jpg


这20个图片对应了20个不同的特征映射。每个映射是一个5×5的图像,对应于局部感知域的5×5个权重。颜色越白(浅)说明权值越小(一般都是负的),因此对应像素对于识别这个特征越不重要。颜色越深(黑)说明权值越大,对应的像素越重要。

那么我们可以从这些特征映射里得出什么结论呢?很显然这里包含了非随机的空间结构。这说明我们的网络学到了一些空间结构。但是,也很难说它具体学到了哪些特征。我们学到的不是一个 Gabor滤波器 的。事实上有很多研究工作尝试理解机器到底学到了什么样的特征。如果你感兴趣,可以参考Matthew Zeiler 和 Rob Fergus在2013年的论文 Visualizing and Understanding Convolutional Networks。

共享权重和bias的一大好处是它极大的减少了网络的参数数量。对于每一个特征映射,我们只需要 25=5×5 个权重,再加一个bias。因此一个特征映射只有26个参数。如果我们有20个特征映射,那么只有20×26=520个参数。如果我们使用全连接的神经网络结构,假设隐藏层有30个神经元(这并不算很多),那么就有784*30个权重参数,再加上30个bias,总共有23,550个参数。换句话说,全连接的网络比卷积网络的参数多了40倍。

当然,我们不能直接比较两种网络的参数,因为这两种模型有本质的区别。但是,凭直觉,由于卷积网络有平移不变的特性,为了达到相同的效果,它也可能使用更少的参数。由于参数变少,卷积网络的训练速度也更快,从而相同的计算资源我们可以训练更深的网络。

“卷积”神经网络是因为公式(1)里的运算叫做“卷积运算”。更加具体一点,我们可以把公式(1)里的求和写成卷积:$a^1 = \sigma(b + w * a^0)$。*在这里不是乘法,而是卷积运算。这里不会讨论卷积的细节,所以读者如果不懂也不要担心,这里只不过是为了解释卷积神经网络这个名字的由来。【建议感兴趣的读者参考colah的博客文章 《Understanding Convolutions》】

池化(Pooling)

除了上面的卷积层,卷积神经网络也包括池化层(pooling layers)。池化层一般都直接放在卷积层后面池化层的目的是简化从卷积层输出的信息。

更具体一点,一个池化层把卷积层的输出作为其输入并且输出一个更紧凑(condensed)的特征映射。比如,池化层的每一个神经元都提取了之前那个卷积层的一个2×2区域的信息。更为具体的一个例子,一种非常常见的池化操作叫做Max-pooling。在Max-Pooling中,这个神经元选择2×2区域里激活值最大的值,如下图所示:

008.jpg


注意卷积层的输出是24×24的,而池化后是12×12的。

就像上面提到的,卷积层通常会有多个特征映射。我们会对每一个特征映射进行max-pooling操作。因此,如果一个卷积层有3个特征映射,那么卷积加max-pooling后就如下图所示:

009.jpg


我们可以把max-pooling看成神经网络关心某个特征在这个区域里是否出现。它忽略了这个特征出现的具体位置。直觉上看,如果某个特征出现了,那么这个特征相对于其它特征的精确位置是不重要的【精确位置不重要,但是大致的位置是重要的,比如识别一个猫,两只眼睛和鼻子有一个大致的相对位置关系,但是在一个2×2的小区域里稍微移动一下眼睛,应该不太影响我们识别一只猫,而且它还能解决图像拍摄角度变化,扭曲等问题】。而且一个很大的好处是池化可以减少特征的个数【2×2的max-pooling让特征的大小变为原来的1/4】,因此减少了之后层的参数个数。

Max-pooling不是唯一的池化方法。另外一种常见的是L2 Pooling。这种方法不是取2×2区域的最大值,而是2×2区域的每个值平方然后求和然后取平方根。虽然细节有所不同,但思路和max-pooling是类似的:L2 Pooling也是从卷积层压缩信息的一种方法。在实践中,两种方法都被广泛使用。有时人们也使用其它的池化方法。如果你真的想尝试不同的方法来提供性能,那么你可以使用validation数据来尝试不同池化方法然后选择最合适的方法。但是这里我们不在讨论这些细节。【Max-Pooling是用的最多的,甚至也有人认为Pooling并没有什么卵用。深度学习一个问题就是很多经验的tricks由于没有太多理论依据,只是因为最早的人用了,而且看起来效果不错(但可能换一个数据集就不一定了),所以后面的人也跟着用。但是过了没多久又被认为这个trick其实没啥用】

放到一起

现在我们可以把这3个idea放到一起来构建一个完整的卷积神经网络了。它和之前我们看到的结构类似,不过增加了一个有10个神经元的输出层,这个层的每个神经元对应于0-9直接的一个数字:


010.jpg


这个网络的输入的大小是28×28,每一个输入对于MNIST图像的一个像素。然后使用了3个特征映射,局部感知域的大小是5×5。这样得到3×24×24的输出。然后使用对每一个特征映射的输出应用2×2的max-pooling,得到3×12×12的输出。

最后一层是全连接的网络,3×12×12个神经元会连接到输出10个神经元中的每一个。这和之前介绍的全连接神经网络是一样的。

卷积结构和之前的全连接结构有很大的差别。但是整体的图景是类似的:一个神经网络有很多神经元,它们的行为有weights和biase确定。并且整体的目标也是类似的:使用训练数据来训练网络的weights和biases使得网络能够尽量好的识别图片。

和之前介绍的一样,这里我们仍然使用随机梯度下降来训练。不过反向传播算法有所不同。原因是之前bp算法的推导是基于全连接的神经网络。不过幸运的是求卷积和max-pooling的导数是非常简单的。如果你想了解细节,请自己推导。【这篇文章不会介绍CNN的梯度求解,后面实现使用的是theano,后面介绍CS231N的CNN是会介绍怎么自己来基于自动求导来求这个梯度,而且还会介绍高效的算法,感兴趣的读者请持续关注】

CNN实战

前面我们介绍了CNN的基本理论,但是没有讲怎么求梯度。这里的代码是用theano来自动求梯度的。我们可以暂时把cnn看出一个黑盒,试试用它来识别MNIST的数字。后面的文章会介绍theano以及怎么用theano实现CNN。

代码

首先得到代码: git clone

安装theano

参考这里 ;如果是ubuntu的系统,可以参考这里 ;如果您的机器有gpu,请安装好cuda以及让theano支持gpu。

默认的network3.py的第52行是 GPU = True,如果您的机器没有gpu,请把这一行改成GPU = False

baseline

首先我们实现一个baseline的系统,我们构建一个只有一个隐藏层的3层全连接网络,隐藏层100个神经元。我们训练时60个epoch,使用learning rate $\eta = 0.1$,batch大小是10,没有正则化:
$cd src
$ipython
>>> import network3
>>> from network3 import Network
>>> from network3 import ConvPoolLayer, FullyConnectedLayer, SoftmaxLayer
>>> training_data, validation_data, test_data = network3.load_data_shared()
>>> mini_batch_size = 10
>>> net = Network([
FullyConnectedLayer(n_in=784, n_out=100),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1,
validation_data, test_data)
得到的分类准确率是97.8%。这是在test_data上的准确率,这个模型使用训练数据训练,并根据validation_data来选择当前最好的模型。使用validation数据来可以避免过拟合。读者运行时可能结果会有一些差异,因为模型的参数是随机初始化的。

改进版本1

我们首先在输入的后面增加一个卷积层。我们使用5 5的局部感知域,stride等于1,20个特征映射。然后接一个2 2的max-pooling层。之后接一个全连接的层,最后是softmax(仿射变换加softmax):

011.jpg


在这种网络结构中,我们可以认为卷积和池化层可以学会输入图片的局部的空间特征,而全连接的层整合全局的信息,学习出更抽象的特征。这是卷积神经网络的常见结构。

下面是代码:
>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2)),
FullyConnectedLayer(n_in=20*12*12, n_out=100),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1,
validation_data, test_data)
【注意图片的大小,开始是(mini_batch_size, 1, 28 ,28),经过一个20个5 5的卷积池层后变成了(mini_batch_size, 20, 24,24),然后在经过2 2的max-pooling后变成了(mini_batch_size, 20, 12, 12),然后接全连接层的时候可以理解成把所以的特征映射展开,也就是20 12 12,所以FullyConnectedLayer的n_in是20 12 12】

这个模型得到98.78%的准确率,这相对之前的97.8%是一个很大的提高。事实上我们的错误率减少了1/3,这是一个很大的提高。【准确率很高的时候就看错误率的减少,这样比较有成就感,哈哈】

如果要用gpu,可以把上面的命令保存到一个文件test.py,然后:
$THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python test.py 
在这个网络结构中,我们吧卷积和池化层看出一个整体。这只是一种习惯。network3.py会把它们当成一个整体,每个卷积层后面都会跟一个池化层。但实际的一些卷积神经网络并不都要接池化层。

改进版本2

我们再加入第二个卷积-池化层。这个卷积层插入在第一个卷积层和全连接层中间。我们使用同样的5×5的局部感知域和2×2的max-pooling。代码如下:
>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2)),
ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),
filter_shape=(40, 20, 5, 5),
poolsize=(2, 2)),
FullyConnectedLayer(n_in=40*4*4, n_out=100),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1,
validation_data, test_data)
【注意图片的大小,开始是(mini_batch_size, 1, 28 ,28),经过一个20个5 5的卷积池层后变成了(mini_batch_size, 20, 24,24),然后在经过2 2的max-pooling后变成了(mini_batch_size, 20, 12, 12)。然后是40个5*5的卷积层,变成了(mini_batch_size, 40, 8, 8),然后是max-pooling得到(mini_batch_size, 40, 4, 4)。然后是全连接的层】 
这个模型得到99.6%的准确率!

这里有两个很自然的问题。第一个是:加第二个卷积-池化层有什么意义呢?事实上,你可以认为第二个卷积层的输入是12*12的”图片“,它的”像素“代表某个局部特征。【比如你可以认为第一个卷积层识别眼睛鼻子,而第二个卷积层识别脸,不同生物的脸上面鼻子和眼睛的相对位置是有意义的】

这是个看起来不错的解释,那么第二个问题来了:第一个卷积层的输出是不同的20个不同的局部特征,因此第二个卷积层的输入是20 12 12。这就像我们输入了20个不同的”图片“,而不是一个”图片“。那第二个卷积层的神经元学到的是什么呢?【如果第一层的卷积网络能识别”眼睛“,”鼻子“,”耳朵“。那么第二层的”脸“就是2个眼睛,2个耳朵,1个鼻子,并且它们满足一定的空间约束。所以第二层的每一个神经元需要连接第一层的每一个输出,如果第二层只连接”眼睛“这个特征映射,那么只能学习出2个眼睛,3个眼睛这样的特征,那就没有什么用处了】

改进版本3

使用ReLU激活函数。ReLU的定义是:


ReLU(x)=max(0,x)


>>> from network3 import ReLU
>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),
filter_shape=(40, 20, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.03,
validation_data, test_data, lmbda=0.1)
使用ReLU后准确率从99.06%提高到99.23%。从作者的经验来看,ReLU总是要比sigmoid激活函数要好。

但为什么ReLU就比sigmoid或者tanh要好呢?目前并没有很好的理论介绍。ReLU只是在最近几年开始流行起来的。为什么流行的原因是经验:有一些人尝试了ReLU,然后在他们的任务里取得了比sigmoid好的结果,然后其他人也就跟风。理论上没有人证明ReLU是更好的激活函数。【所以说深度学习有很多tricks,可能某几年就流行起来了,但过几年又有人认为这些tricks没有意义。比如最早的pretraining,现在几乎没人用了。】

改进版本4

扩展数据。

深度学习非常依赖于数据。我们可以根据任务的特点”构造“新的数据。一种简单的方法是把训练数据里的数字进行一下平移,旋转等变换。虽然理论上卷积神经网络能学到与位置无关的特征,但如果训练数据里数字总是出现在固定的位置,实际的模型也不一定能学到。所以我们构造一些这样的数据效果会更好。
$ python expand_mnist.py
expand_mnist.py这个脚本就会扩展数据。它只是简单的把图片向上下左右各移动了一个像素。扩展后训练数据从50000个变成了250000个。

接下来我们用扩展后的数据来训练模型:
>>> expanded_training_data, _, _ = network3.load_data_shared(
"../data/mnist_expanded.pkl.gz")
>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),
filter_shape=(40, 20, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(expanded_training_data, 60, mini_batch_size, 0.03,
validation_data, test_data, lmbda=0.1)
这个模型的准确率是99.37%。扩展数据看起来非常trival,但是却极大的提高了识别准确率。

改进版本5

接下来还有改进的办法吗?我们的全连接层只有100个神经元,增加神经元有帮助吗? 作者尝试了300和1000个神经元的全连接层,得到了99.46%和99.43%的准确率。相对于99.37%并没有本质的提高。

那再加一个全连接的层有帮助吗?我们了尝试一下:
>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),
filter_shape=(40, 20, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),
FullyConnectedLayer(n_in=100, n_out=100, activation_fn=ReLU),
SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(expanded_training_data, 60, mini_batch_size, 0.03,
validation_data, test_data, lmbda=0.1)
在第一个全连接的层之后有加了一个100个神经元的全连接层。得到的准确率是99.43%,把这一层的神经元个数从100增加到300个和1000个得到的准确率是99.48 %和99.47%。有一些提高但是也不明显。

为什么增加更多层提高不多呢,按说它的表达能力变强了,可能的原因是过拟合。那怎么解决过拟合呢?一种方法就是dropout。drop的详细解释请参考这里。简单来说,dropout就是在训练的时候随机的让一些神经元的激活“丢失”,这样网络就能学到更加鲁棒的特征,因为它要求某些神经元”失效“的情况下网络仍然能工作,因此就不会那么依赖某一些神经元,而是每个神经元都有贡献。

下面是在两个全连接层都加入50%的dropout:
>>> net = Network([
ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),
filter_shape=(20, 1, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),
filter_shape=(40, 20, 5, 5),
poolsize=(2, 2),
activation_fn=ReLU),
FullyConnectedLayer(
n_in=40*4*4, n_out=1000, activation_fn=ReLU, p_dropout=0.5),
FullyConnectedLayer(
n_in=1000, n_out=1000, activation_fn=ReLU, p_dropout=0.5),
SoftmaxLayer(n_in=1000, n_out=10, p_dropout=0.5)],
mini_batch_size)
>>> net.SGD(expanded_training_data, 40, mini_batch_size, 0.03,
validation_data, test_data)
使用dropout后,我们得到了99.60%的一个模型。

这里有两点值得注意:
  • 训练的epoch变成了40.因为dropout减少了过拟合,所以我们不需要60个epoch。
  • 全连接层使用了1000个神经元。因为dropout会丢弃50%的神经元,所以从直觉来看1000个神经元也相当于只有500个。如果过用100个神经元感觉太少了点。作者经过验证发现有了dropout用1000个比300个的效果好。


改进版本6

ensemble多个神经网络。作者分别训练了5个神经网络,每一个都达到了99.6%的准确率,然后用它们来投票,得到了99.67%准确率的模型。

这是一个非常不错的模型了,10000个测试数据只有33个是错误的,我们把错误的图片都列举了出来:

012.jpg


图片的右上角是正确的分类,右下角是模型的分类。可以发现有些错误可能人也会犯,因为有些数字人也很难分清楚。

【为什么只对全连接的层使用dropout?】

如果读者仔细的阅读代码,你会发现我们只对全连接层进行了dropout,而卷积层没有。当然我们也可以对卷积层进行dropout。但是没有必要。因为卷积层本身就有防止过拟合的能力。原因是权值共享强制网络学到的特征是能够应用到任何位置的特征。这让它不太容易学习到特别局部的特征。因此也就没有必要对它进行的dropout了。

更进一步

感兴趣的读者可以参考这里,列举了MNIST数据集的最好结果以及对应的论文。目前最好的结果是99.79%

What’s Next?

接下来的文章会介绍theano,一个非常流行的深度学习框架,然后会讲解network3.py,也就是怎么用theano实现CNN。敬请关注。 收起阅读 »

李理:从Image Caption Generation理解深度学习(part III)

本系列文章面向程序员,希望通过Image Caption Generation,一个有意思的具体任务,深入浅出地介绍深度学习的知识,涉及到很多深度学习流行的模型,如CNN,RNN/LSTM,Attention等。本文为第三篇。 作者:李理,目前就职于环信,即...
继续阅读 »
本系列文章面向程序员,希望通过Image Caption Generation,一个有意思的具体任务,深入浅出地介绍深度学习的知识,涉及到很多深度学习流行的模型,如CNN,RNN/LSTM,Attention等。本文为第三篇。

作者:李理,目前就职于环信,即时通讯云平台和全媒体智能客服平台,在环信从事智能客服和智能机器人相关工作,致力于用深度学习来提高智能机器人的性能。 
相关文章: 
从Image Caption Generation理解深度学习(part I)
从Image Caption Generation理解深度学习(part II)
2.2.5 反向传播算法的推导

前面我们用很简单的几十行python代码基本上完成了一个多层神经网络。但是还差最重要的部分,那就是计算loss function对参数的偏导数,也就是反向传播算法。下面我们来仔细的完成公式的推导,以及接下来会讲怎么用代码来实现。这一部分数学公式多一些,可能很多读者会希望跳过去,不过我还是建议大家仔细的阅读,其实神经网络用到的数学相比svm,bayes network等机器学习算法,已经非常简单了。请读者阅读的时候最好准备一支笔和几张白纸,每一个公式都能推导一下。如果坚持下来,你会觉得其实挺简单的。

(1) feedforward阶段的矩阵参数表示和计算

之前我们讨论的是一个神经元的计算,而在代码里用到的却是矩阵向量乘法。而且细心的读者会发现我们在构造参数矩阵weights的时候,行数和列数分别是后一层的节点数和前一层的节点数。这似乎有点不自然,为什么不反过来呢?看过下面这一部分就会明白了。

001.jpg


首先我们熟悉一下第L(因为小写的L和1太像,所以我用大写的L)层的参数w_jk。它表示第L-1层的第k个神经元到第L层的第j个神经元的权重。比如第3层的w_24,参考上面的图,它表示的是第2层的第4个神经元到第3层的第二个神经元。

对bias和激活函数后的结果a也采用类似的记号,如下图所示。

002.jpg


b_32表示第2层的第3个神经元的bias,而a_13第3层的第1个神经元的激活。

使用上面的记号,我们就可以计算第L层的第j个神经元的输出a_jl:

003.jpg


第L层的第j个神经元的输入是L-1层的a_1,a_2,...;对应的权值是w_j1,w_j2,...;bias是b_jL。所以a_jL就是上面的公式,k的范围是从1到第L-1层的神经元的个数。 
为了用矩阵向量乘法来一次计算第L层的所有神经元的输出,我们需要定义第L层的参数矩阵w_l,它的大小是m*n,其中m是第L层的神经元个数;而n则是第L-1层的个数。它的第i行第j列就是我们上面定义的w_jk。此外我们还要定义向量b_l,它的大小是m(也就是第L层神经元的个数),它的第j个元素就是我们上面定义的b_j。 
最后,我们定义element-wise的函数,比如f(x) = x^2,如果输入是一个向量,那么结果是和输入一样大小的向量,它的每个元素是对输入向量的每一个元素应用这个函数的结果。

004.jpg


有了上面的定义,我们就可以一次计算出第L层的输出(一个长度为m的向量)

005.jpg


下面是对上面这个公式的详细证明(说明): 
我们需要证明的是向量aL的第j个元素就是前面的a_jL

006.jpg


此外,为了方便后面的求解,我们把加权累加和也用一个符号z_l来表示。

007.jpg


其中,它的第j个元素就是第L层的第j个神经元的加权累加和:

008.jpg


这样a_l就可以简单的对z_l的每个元素计算激活函数

009.jpg


现在我们再回顾一下feedforward的代码就非常直观了:
def feedforward(self, a):
"""Return the output of the network if a is input."""
for b, w in zip(self.biases, self.weights):
a = sigmoid(np.dot(w, a)+b)
return a
传给函数feedforward的参数a就是输入向量x,第一层就是x,第二层就是第一个隐层,每一层的计算就是非常简单的参数矩阵w_l乘以上一层的激活a_l-1在加上b_l,然后用激活函数计算。

初始化的时候w的大小是 (后一层的神经元个数) * (前一层的神经元个数),再回顾一下初始化参数的代码:
# sizes = [784, 30, 10]
def __init__(self, sizes):
self.num_layers = len(sizes)
self.sizes = sizes
self.biases = [np.random.randn(y, 1) for y in sizes[1:]]
self.weights = [np.random.randn(y, x)for x, y in zip(sizes[:-1], sizes[1:])]
x, y in zip(sizes[:-1], sizes[1:]) x是第一层到最后倒数第二层,y是第二层到最后一层,比如上面的sizes=[784, 30, 10] 
x是[784, 30], y是[30, 10],注意随机的矩阵是(y,x),所以self.weights是两个矩阵,大小分别是30*784和10*30

(2) 关于损失函数C的两个假设 
1. 损失函数是每个训练数据的损失的平均 
也就是C是这样的形式:

010.jpg


对于之前我们使用的MSE损失函数,这是满足的。我们使用batch的梯度下降的时候需要求C对参数w的偏导数,因为损失函数是每个训练数据的损失的平均,所以我们只需要求每个数据的偏导数,然后加起来平均就行。这个假设几乎所有的损失函数都是满足的【我是没见过损失函数不满足这个条件】

损失函数是最后一层输出的函数

011.jpg


这个条件几乎常见的损失函数都是这样的,我们之前时候的MSE就是计算最后一层的输出aL和正确的y(one-hot)的均方误差,显然是满足的。

(3) Hadamard product 
这个名字看起来很复杂,其实很简单,就是两个向量elementwise的乘法。看一个例子就清楚了:

012.jpg


(4) 反向传播算法(back propagation)的4个公式

回顾一下,我们之前说了,梯度下降其实最核心的问题就是求损失函数对每一个参数的偏导数。那我们就直接一个一个求好了,为什么又要搞出一个反向传播算法呢?其实这个算法在不同的领域被不同的人重复“发现”过很多次,有过很多不同的名字,最本质的应该就是逆向求导(reverse-mode differentiation)或者叫做自动求导(automatic differentiation)。自动求导(AD)是非常通用的一种求偏导数的方法,很早就在流体力学和大气物理等领域使用,反向传播算法可以认为是AD在神经网络中的应用。不过最早发现这个算法的人(是谁最早好像还有点争议)并不是先知道AD可以直接用于神经网络,他发现这个算法是基于错误的反向传播而得到的,所有命名为(错误的)反向传播算法。后面我们会讲到AD,这是一个强大的算法,任何一个函数,你能把它分解成有向无环图的计算图【函数一般都能分解成一些无依赖的最基础的变量的复合函数,因此肯定可以表示成这样一个有向无环图】,然后每个节点都表示一个函数。只要你能求出这个函数在特定点的梯度【也就是这个函数对所以自变量的偏导数】(不需要求解析的偏导数,当然很多情况,这些函数都是能直接求出解析解,然后代入这个特定点就行,但理论上我们是可以用其他方法,比如数值梯度近似来求的),就能自动的计算损失函数对每一个参数的偏导数(也是在这个点的),而且只要反向根据拓扑排序遍历这个图一次就行,非常高效和简单。后面我们会详细的介绍AD。这个方法非常通用,TensorFlow的核心就是AD。使用AD的框架就比较灵活,我想“创造”一种新的网络结构,我又不想【其实更可能是不会】推导出梯度的公式,那么我只需要把我的网络能用这样一个有向无环图表示就行。当然节点必须要能够求出梯度来,一般我们的函数比如矩阵的运算,卷积等等TensorFlow都封装好了——它把它叫做一个op。我们只需要搭积木一样把这个计算图定义出来,TensorFlow就自动的能根据AD计算出损失函数对所有参数的梯度来了。当然如果你要用到一个TensorFlow没有的op,那你就需要根据它的规范实现这个op,一个op最核心的接口就是两个,一个是输入x,求f(x);另一个就是求f在某个x0点的梯度。

不过这里,我们还是沿着神经网络的发展历史,从错误的反向传播角度来理解和推导这个算法。

首先,我们会对每一个神经元比如第L层的第j个,都定义一个错误δ_jL

013.jpg


也就是损失函数对z也就是线性累加和的偏导数。为什么定义这样一个东西呢?我们假设在第L层的第j个神经元上有一个精灵(Daemon)

014.jpg


当这个神经元得到来自上一次的输入累加计算出z_jL的时候,它会恶作剧的给一点很小的干扰Δz_jL。原来它应该输出的是σ(z_jL),现在变成了σ(z_jL +Δz_jL)。这个微小的变化逐层传播,最终导致损失函数C也发生如下的变化:

015.jpg


这个其实就是导数的直觉定义:微小的Δx引起微小的Δy,Δy/Δx约等于导数。 
不过这个精灵是个好精灵,它想帮助我们减少损失。 当

016.jpg


大于0的时候,它让Δz_jL小于0,反之当它小于0的时候它让Δz_jL大于0。这样

017.jpg


总是小于0 
因此我们的loss就会变小。而其绝对值越大,我们的损失减少的越多。 
当然你会说为什么不能让Δz_jL非常大,这样我们的损失总是减少很多?可惜这个精灵是个数学家,它说如果Δx太大,那么Δy=df/dx *Δx就不准确了。

所以我们可以这样认为:它就是第L层的第j个神经元“引起”的“错误”。如果绝对值大,则它的“责任”也大,它就得多做出一些调整;反之如果它趋近于0,说明它没有什么“责任”,也就不需要做出什么改变。 
因此通过上面的启发,我们定义出δ_jL来。

018.jpg


接下来我们逐个介绍反向传播算法的4个公式。

公式1. 第L层(最后一层) 的错误

019.jpg


这个公式的第一项,就是损失C对a_jL的导数,它越大,说明C受a_jL的影响也就越大,如果有了错误,第a_jL的“责任”也就越大,错误也就越大。第二项是a_jL受z_jL的影响。两者乘起来就是z_jL对最终损失的影响,也就是它的“责任”的大小。

这个公式很好计算,首先第二项就是把z_jL的值(这个在feedforward节点就算出来并存储下来了)代入σ'(x)。如果σ是sigmoid函数,我们前面也推导过它的导数:σ’(x)=σ(x)*(1-σ(x))。第一项当然依赖于损失函数的定义,一般也很好求。比如我们的MSE损失:

020.jpg


具体的推导我在纸上写了一下,虽然很简单,我们也可以练练手,尤其是对于求和公式的展开,希望大家能熟悉它,以后的推导我可能就不展开求和公式了,你需要知道求和公式里哪些项是和外面的自变量无关的。

021.jpg


公式BP1是elementwise的,我们需要变量j来计算每一个δ_jL。我们也可以把它写成向量的形式,以方便利用线性代数库,它们可以一次计算向量或者矩阵,可以用很多技术利用硬件特性来优化(包括GPU,SSE等)速度。

022.jpg


右边δ'(z_L)很容易理解,左边的记号可能有些费解,其实我们把∇aC当成一个整体就好了,它是一个向量,第一个元素是∂C/∂a_1L,第二个就是∂C/∂a_2L,… 
如果算上函数C是MSE的话,上面的公式就可以简化成:

023.jpg


公式2. 第l层(非最后一层) 的错误

024.jpg


等下我们会证明这个公式,不过首先我们来熟悉一下公式。如果我们想“背”下这个公式的话,似乎看起来比第一个BP1要复杂很多 。我们先检查一下矩阵和向量的维度,假设l+1层有m个元素,l层n个。则w_l+1的大小是m*n,转置之后是n*m,δ_l+1的大小是n*1,所以矩阵相乘后是m*1,这和δ_l是一样的,没有问题。

接下来我们仔细观察一下BP2这个公式,首先第二项σ'(z_l)和前面的含义一样,代表a_l对于z_l的变化率。 
而第一项复杂一点,我们知道第l层的第j个神经元会影响第l+1层的所有神经元,从而也影响最终的损失C。这个公式直接给了一个矩阵向量的形式,看起来不清楚,所以我在草稿纸上展开了:

025.jpg


最终第L层的第j个神经元的损失就是如下公式:

026.jpg


这下应该就比较清楚了,第l层的第j个神经元的损失,就是把l+1层的损失“反向传播”回来,当然要带上权重,权重越大,“责任”也就越大。

如果要“背”出这个公式也没有那么复杂了,先不看σ'(z_l),第一项应该是矩阵w_l+1乘以δ_l+1。由于矩阵是m*n,而 
向量δ_l+1是m*1,为了能让矩阵乘法成立,那么就只能把w转置一下,变成n*m,然后就很容易记住这个公式了。

注意,BP2的计算是从后往前的,首先根据BP1,最后一层的δ_L我们已经算出来了,因此可以向前计算L-1层的δ_L-1, 
有了δ_L-1就能计算δ_L-2,…,最终能算出第一个隐层(也就是第2层)δ_1来。

公式3. 损失函数对偏置b的梯度

这前面费了大力气求δ_l,不要忘了我们的最终目标是求损失函数对参数w和b的偏导数,而不是求对中间变量z的偏导数。

因此这个公式就是对b的偏导数。

027.jpg


或者写成向量的形式:

028.jpg


∂C/∂b就是δ!

公式4. 损失函数对w的梯度

029.jpg


或者参考下图写成好记的形式:

030.jpg


也就是说对于一条边w_jkL,∂C/∂w_ij就是这条边射出的点的错误δ乘以进入点的激活。非常好记。

我们把这四个公式再总结一下:

031.jpg


(5) 这四个公式的证明

首先是BP1,请参考下图:

032.jpg


剩下的BP3和BP4也非常类似,我就不证明了。

反向传播算法 
1. a_1 = 输入向量x 
2. Feedforward 根据公式

033.jpg


和

034.jpg


计算z_l和a_l并存储下来(反向传播时要用的) 
3. 计算最后一层的错误

035.jpg


计算损失对所有参数的偏导数

036.jpg


2.2.6 代码实现反向传播算法

我们已经把公式推导出来了,那怎么用代码实现呢?我们先把代码复制一下,然后说明部分都是作为代码的注释了, 
请仔细阅读。
class Network(object):
def update_mini_batch(self, mini_batch, eta):
# mini_batch是batch大小,eta是learning rate

nabla_b = [np.zeros(b.shape) for b in self.biases]
# 构造和self.biases一样大小的向量,比如前面的例子 sizes=[784,30,10],则
# nabla_b是两个向量,大小分别是30和10

nabla_w = [np.zeros(w.shape) for w in self.weights]
# 构造和self.weights一样大小的矩阵,比如前面的例子 sizes=[784,30,10],则
# nabla_w是两个矩阵,大小分别是30*784和10*30

for x, y in mini_batch: #对于每个训练样本x和y
delta_nabla_b, delta_nabla_w = self.backprop(x, y)
# 用backprop函数计算损失函数对每一个参数的偏导数。
# backprop函数下面会详细讲解

nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
# 把返回的对b偏导数累加到nabla_b中

nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
# 把返回的对w的偏导数累加到nabla_w中

self.weights = [w-(eta/len(mini_batch))*nw
for w, nw in zip(self.weights, nabla_w)]
# 计算完一个batch后更新参数w

self.biases = [b-(eta/len(mini_batch))*nb
for b, nb in zip(self.biases, nabla_b)]
# 更新b
...
def backprop(self, x, y):
# 输入是x和y,返回损失函数C对每个参数w和b的偏导数
# 返回的格式是两个元组,第一个是b的偏导数,第二个是w的。
nabla_b = [np.zeros(b.shape) for b in self.biases]
# 构造和self.biases一样大小的向量,比如前面的例子 sizes=[784,30,10],则
# nabla_b是两个向量,大小分别是30和10

nabla_w = [np.zeros(w.shape) for w in self.weights]
# 构造和self.weights一样大小的矩阵,比如前面的例子 sizes=[784,30,10],则
# nabla_w是两个矩阵,大小分别是30*784和10*30

# feedforward
activation = x
activations = [x] # 用一个list保存所有层的激活,下面backward会有用的
zs = # 同样的用一个list保存所有层的加权累加和z,下面也会用到。

#下面这段代码在feedward也有,不过那里是用来predict用的不需要保存zs和activations
for b, w in zip(self.biases, self.weights):
z = np.dot(w, activation)+b
zs.append(z)
activation = sigmoid(z)
activations.append(activation)

# backward pass
#1. 首先计算最后一层的错误delta,根据公式BP1,它是损失函数对a_L的梯度乘以σ'(z_L)
# sigmoid_prime就是σ'(z_L),而∂C/∂a_L就是函数cost_derivative,对于MSE的损失函数,
# 它就是最后一层的激活activations[-1] - y
delta = self.cost_derivative(activations[-1], y) * \
sigmoid_prime(zs[-1])
# 2. 根据公式BP3,损失对b的偏导数就是delta
nabla_b[-1] = delta
# 3. 根据公式BP4,损失对w的偏导数时delta_out * activation_in
# 注意,我们的公式BP4是elementwise的,我们需要写成矩阵向量的形式
# 那怎么写呢?我们只需要关心矩阵的大小就行了。
# 假设最后一层有m(10)个神经元,前一层有n(30)个,
# 则delta是10*1, 倒数第二层的激活activations[-2]是30*1
# 我们想求的最后一层的参数nabla_w[-1]是10*30,那么为了能够正确的矩阵乘法,
# 只要一种可能就是 delta 乘以 activations[-2]的转置,其实也就是向量delta和activations[-2]的外积
nabla_w[-1] = np.dot(delta, activations[-2].transpose())

# 接下来从倒数第二层一直往前计算delta,同时也把对w和b的偏导数求出来。
# 这里用到一个比较小的trick就是python的下标是支持负数的,-1表示最后一个元素,-2是倒数第二个
# l表示倒数第l层,2就表示倒数第2层,num_layers - 1就表示顺数第2层(也就是第1个隐层)
# 比如我们的例子:sizes=[784, 30, 10],那么l就是从2到3(不包含3),l就只能是2,页就是第1个(也是唯一的一
# 个)隐层
for l in xrange(2, self.num_layers):
# 倒数第l层的z
z = zs[-l]
# 计算σ'(z_l)
sp = sigmoid_prime(z)
# 根据BP2,计算delta_l,注意weights[-l+1]表示倒数第l层的下一层
delta = np.dot(self.weights[-l+1].transpose(), delta) * sp
# 同上,根据BP3
nabla_b[-l] = delta
# BP4,矩阵乘法参考前面的说明
nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
return (nabla_b, nabla_w)
2.2.7 为什么反向传播算法是一个高效的算法?

分析完代码,我们发现一次backprop函数调用需要feedforward一次,网络有多少边,就有多少次乘法,有多少个点就有多少次加分和激活函数计算(不算第一层输入层)。反向计算也是一样,不过是从后往前。也就是说这是时间复杂度为O(n)的算法。 
如果我们不用反向传播算法,假设我们用梯度的定义计算数值梯度。对于每一个参数wj,

我们都用公式 limit (f(w1, w2, …, wj+Δ wj, …) - f(w1, w2, …, wj, …)/Δwj

f(w1, w2, wj, …)只需要feedforward一次,但是对于每个参数wj,都需要feedforward一层来计算f(w1, w2, …, wj+Δ wj, …),它的时间复杂度是O(n),那么对所有的参数的计算需要O(n^2)的时间复杂度。

假设神经网络有1百万个参数,那么每次需要10^12这个数量级的运算,而反向传播算法只需要10^6,因此这个方法比反向传播算法要慢1百万倍。 收起阅读 »

【公告】环信IOS支持https的版本正式发布

        因苹果公司从2017.1.1日起强制要求所有上线APPStore的APP都需要支持ATS标准。为了适应这一政策不影响用户APP的正常发布,环信分别于2016.12.6日和8日正式发布IOS V2.2.9和V3.2.2版本,请尽快更新SDK的版本...
继续阅读 »
   
    因苹果公司从2017.1.1日起强制要求所有上线APPStore的APP都需要支持ATS标准。为了适应这一政策不影响用户APP的正常发布,环信分别于2016.12.6日和8日正式发布IOS V2.2.9和V3.2.2版本,请尽快更新SDK的版本以免影响APP的正常发布计划。

https_-_副本.jpg



ios V3.2.2 SDK 已发布,增加是否删除会话选项
 
新功能/优化: 


SDK满足apple ATS的要求

删除好友逻辑的修改(增加是否删除会话选项)

修复呼叫时对方不在线,不能正确显示通话结束原因的问题



ios V2.2.9 SDK 已发布,SDK满足apple ATS的要求

 新功能/优化:


SDK满足apple ATS的要求

删除好友逻辑的修改(增加是否删除会话选项)

修复呼叫时对方不在线,不能正确显示通话结束原因的问题


新版SDK下载:SDK下载

更新过程中遇到问题欢迎社区发帖咨询或者联系环信技术支持 收起阅读 »

客户中心的“手艺人”

属于21世界的新工匠,应该是懂得关心他人、知道感恩、能为别人着想的人,是能够说“好的,明白了,请交给我来做”的人,也就是拥有一流人品、“会好好做事”的匠人。 一流的匠人,人品比技术更重要。 《匠人精神》秋山利辉     “劳动密集”,几乎是对客户...
继续阅读 »
属于21世界的新工匠,应该是懂得关心他人、知道感恩、能为别人着想的人,是能够说“好的,明白了,请交给我来做”的人,也就是拥有一流人品、“会好好做事”的匠人。

一流的匠人,人品比技术更重要。

《匠人精神》秋山利辉
  

5896.jpg_wh860_-_副本_.jpg


  “劳动密集”,几乎是对客户中心一种“约定俗成”的说法,以至于许多业内的文章都以“这是一个劳动密集型的行业……”开篇,以至于每每聊到一个不熟悉的客户中心,人们必谈其规模,“嗨,你们那里有多少人……?”

   曾经,我也这样认为,也不假思索地一次次这样说着,“嗯,这是一个劳动密集型行业……”。

   蓬勃发展的CTI技术,早已让铃声不似家中电话那样响声外泄;专业的客服职场空间设计,让越来越多的中心逼格大增,各种消音、吸音、采光、通风以及人性化的设置,也让如今的客户中心不再像走进蜂窝那样,“人声鼎沸”、“沉闷无趣”;而全媒体客户中心的发展演进,又出现了许多不戴耳麦,静静坐在那里狂敲键盘的员工。

客户中心,这个客户心中略显神秘的世界,一直在改变着。

   一次次,被耳边此起彼伏但无比一致的亲切声音所打动。客户中心的这帮孩子们,面带微笑、身姿端正、热情地重复着重复了不知多少遍的对话,那专注讲话的神态,挥在空中边说边比划的双手,正像是一个个“手艺人”,反复雕琢着自己的“作品”,或是声音、或是文字、或是一丝淡淡的微笑,在他与客户的世界里。

他们,是以沟通为一技之长的“手艺人”!

   为了让客户听得清楚明白,客户中心的“手艺人”们,一次次逐字逐句修改话术、一遍遍聆听录音,反复琢磨怎样说以及说什么;为了让客户在有需要的时候更容易找到他们,客户中心的“手艺人”们打通所有的联络渠道,提供7*24小时的不间断服务,甚至包括各种节假日;为了让客户的体验更好,“手艺人”们梳理每一个服务流程,不放过每一个细节,通话时长精确到秒、接通率指标关注到小时、甚至连上厕所都要计算着时间……他们,这些客户中心的坐席代表与管理者们,正是企业全面客户管理系统里的“手艺人”。

   一致性,是客户中心管理的至高追求。哪怕是一个简单业务通知的上传下达,放在几百及至上千人的客户中心,就是一件非常考究的“管理艺术品”:从经理到主管,从主管到组长,从组长到员工,从员工到客户,一传十十传百,百传千千万,最终清晰准确传到成千上万个客户那里,这本就是一项客户中心的手艺人必须修炼的基本功。

    很多人认为客户中心的工作是机械重复的,而客户中心的这些手艺人,对这种重复则有着更深远的理解:手艺人独具的工匠精神,代表着这个以客户为本的时代气质,满满的爱、无微不至、精益求精。他们身上传承的工匠精神,是有信仰的踏实和认真!这个信仰,叫“以客为本”。

   “以客为本”的手艺人精神,其价值在于精益求精,对匠心、精品的坚持和追求。对每一个客户而言,或许微不足道,却改变着整个行业的生态,激励着世界文明的进步。
如今,客户中心的“手艺人”们,有了自己的节日:中国客户管理人节(China Customer Care & Service Professionals Day),定于毎年10月第三周的周二,这一天惯例举办客户世界年度大会,颁发“金耳唛杯”中国最佳客户中心奖项!我们为自己代言,我们向社会发声,我们为中国服务的进步而努力!

致敬,向客户心中所有客户中心的手艺人们。
 
本文刊载于《客户世界》2016年10月刊文章;原文作者郭勇强 收起阅读 »

ios V3.2.2 SDK 已发布,增加是否删除会话选项

ios ​V3.2.2 2016-12-08   新功能/优化:   SDK满足apple ATS的要求 删除好友逻辑的修改(增加是否删除会话选项) 修复呼叫时对方不在线,不能正确显示通话结束原因的问题 版本历史:...
继续阅读 »
ios ​V3.2.2 2016-12-08


2597.jpg_wh860_.jpg


 
新功能/优化:
 


SDK满足apple ATS的要求

删除好友逻辑的修改(增加是否删除会话选项)

修复呼叫时对方不在线,不能正确显示通话结束原因的问题


版本历史:更新日志 
下载地址:SDK下载
收起阅读 »

ios V2.2.9 SDK 已发布,SDK满足apple ATS的要求

ios 版本 V3.2.2 2016-12-08   新功能/优化: SDK满足apple ATS的要求 删除好友逻辑的修改(增加是否删除会话选项) 修复呼叫时对方不在线,不能正确显示通话结束原因的问题 版本历史:更...
继续阅读 »
ios 版本 V3.2.2 2016-12-08

7282.jpg_wh860_.jpg



 
新功能/优化:


SDK满足apple ATS的要求

删除好友逻辑的修改(增加是否删除会话选项)

修复呼叫时对方不在线,不能正确显示通话结束原因的问题


版本历史:更新日志 
下载地址:SDK下载
收起阅读 »

2016中国产业互联网大会,环信移动客服荣获产品及解决方案创新奖

      12月6日,由计世传媒等联合承办主题为“赋能•跨越•重构”2016中国产业互联网大会在京隆重举行。会上,工信部副部长怀进鹏和IDG资本创始合伙人熊晓鸽以及工程院李伯虎院士等均发表重要讲话,怀部长表示:"面对我们传统产业转型升极过程当中所提...
继续阅读 »
  
   12月6日,由计世传媒等联合承办主题为“赋能•跨越•重构”2016中国产业互联网大会在京隆重举行。会上,工信部副部长怀进鹏和IDG资本创始合伙人熊晓鸽以及工程院李伯虎院士等均发表重要讲话,怀部长表示:"面对我们传统产业转型升极过程当中所提出和遇到新的问题,不断创造出中国的最佳方案,中国的最佳实践方式,并由此来带动和推进我们整个产业的发展,将会有重要的意义。" 收起阅读 »

2017创业路上,我们陪你

2015年,市场上的热词是:融资、D轮、A轮、万亿市场…… 2016年,市场的热词成为:裁员、资本寒冬、万亿市场…… 为争夺互联网新格局制高点、行业No1以及中国企业服务这万亿市场,Saas、Paas、Caas服务的创业者们不断涌现。但是他们的创业路却不尽...
继续阅读 »


13期_社区活动.jpg

2015年,市场上的热词是:融资、D轮、A轮、万亿市场……
2016年,市场的热词成为:裁员、资本寒冬、万亿市场……
为争夺互联网新格局制高点、行业No1以及中国企业服务这万亿市场,Saas、Paas、Caas服务的创业者们不断涌现。但是他们的创业路却不尽相同。

   我现任COO是个创业狂,2012年至今,创业三次。2012年创立打车品牌,与滴滴、快滴以及当时的众多打车app抢占中国市场,但始终因为资金不敌滴滴背后的大腾讯,以滴滴成功占领全国打车市场成功,他的品牌失败告终。
后来问及他,这么多创业经历,最大的感触是什么?困难是什么?他说创业要考虑很多事情:创业项目是什么?配置怎样的一个团队?时间节点怎么安排?找投资人、员工激励政策、员工效率、创业失败怎么个退路……

   整个创业过程中,融资固然重要,但是组建原始团队是迫切大事。但组团难就难在好的技术人员,比较难挖。招人的同时,还要兼顾融资、立项、开发、担当半个产品……招了人还要搞一堆社保福利……搞得自己精力被分去了一半。

  • “那为什么当时不找个靠谱的招聘公司,不找个办事效率高的代理注册公司?”

  • “12年的时候招聘公司就那么几家,注册公司有那沟通的时间,都不如自己跑!”



评:多么诚实坦荡、干脆利落的一个创业老板……

   除了COO的惨痛创业经历,创业者的你们普遍存在一个错觉(不好意思泼了冷水),感觉通过互联网的渠道,做好推广、做好产品,用户就会“忽如一夜春分来,千树万树梨花开”的样子。然而除了这个叫微信的软件,几乎没有哪一家的成功这么地一马平川。

   真正能获得最终成功的,不论是不是2B,或2C,又或是B2B2C,还是看是否在整个环境中产生了足够的价值,比如O2O一定是未来,但你的产品并非在这个大帽子底下就一定会成功。
不过,还好……你的创业是在当下移动互联网发达的2016年,不是互联网“欠发达”的2012年……所以,2017年创业,不要让自己被不必要的事情分心啦!

2017年创业,有人陪你了!
创业,有我们没我们,不一样!不一样!不一样!

点击“立即领取”,领取属于你的创业福利礼包(20份大礼包哦,一般人我不告诉ta)!助力你的企业发展!

PS:手机端可直接扫码

1481102926.png

收起阅读 »

ios V3.2.1 SDK 已发布,聊天室列表支持分页获取

ios V3.2.1 2016-11-12   新功能/优化:   聊天室列表支持分页获取 EMOption中usingHttps默认为YES bug fix: 修复Lite版本SDK编译warning的问题   版本历史:更新日志  ...
继续阅读 »
ios V3.2.1 2016-11-12
 

3099.jpg_wh860_.jpg



新功能/优化:
 


聊天室列表支持分页获取

EMOption中usingHttps默认为YES



bug fix:


修复Lite版本SDK编译warning的问题


 
版本历史:更新日志  
下载地址:SDK下载
收起阅读 »

猿生态十城巡回沙龙丨深圳

猿生态十城巡回沙龙丨深圳 时间:2016.12.10 13:30-17:00 地点:深圳市南山区高新科技产业园南区科园路北科大厦4002室 开源中国   活动议程 13: 00 – 13: 30  活动签到 13: 30 – 16: 30  技术分享...
继续阅读 »
猿生态十城巡回沙龙丨深圳

时间:2016.12.10 13:30-17:00

地点:深圳市南山区高新科技产业园南区科园路北科大厦4002室 开源中国

 
活动议程

13: 00 – 13: 30  活动签到

13: 30 – 16: 30  技术分享 + 实战演示

16: 30 – 17: 00  抽奖 + 自由交流


讲师介绍

1.png



2.png



3.png



4.png



5.png



6.png



报名链接
http://devhub.cc/api/h5/activity/c368dbb3c1b52a37344985116d641e2f
  收起阅读 »

Android V3.2.2 SDK 已发布,新增音视频离线通知

Android V3.2.2 2016-12-2 新功能/优化: 新增设置音视频参数及呼叫时对方离线是否发推送的接口新增修改群描述的接口;删除好友时的逻辑修改: 删除好友增加接口,根据参数是否删除消息; 被动被删除时不再删除会话消息, 用户需要删除会话及消息...
继续阅读 »

2387.jpg_wh860_.jpg


Android V3.2.2 2016-12-2

新功能/优化:
  • 新增设置音视频参数及呼叫时对方离线是否发推送的接口
  • 新增修改群描述的接口;
  • 删除好友时的逻辑修改: 删除好友增加接口,根据参数是否删除消息; 被动被删除时不再删除会话消息, 用户需要删除会话及消息时可以在onContactDeleted()中调用EMClient.getInstance().chatManager().deleteConversation(username, true)。
Bug Fix:
  • 修复3.2.1版本中某些情况下心跳比较频繁的问题,节约流量电量,建议升级到最新版本;
  • 修复呼叫时对方不在线,不能正确显示通话结束原因的问题;
  • 修复某些特殊情况下获取群成员列表时crash的问题;
  • 修复某些特殊情况下退出时crash的问题;
Demo:
  • demo中增加音视频参数设置页;

 
版本历史:更新日志  
下载地址:SDK下载
收起阅读 »

【产品快递】Web IM V1.4.5已发布,支持实时视频聊天

   临近新年,12月的第一天,自上次更新以来刚好一个月,环信Web IM V1.4.5已经发布,是不是很准时呢?毕竟想让环信小伙伴们都过上好年。环信Web IM V1.4.5版本最大的改动是增加了实时视频聊天功能,即在网页中打开就可以直接与APP视频通话,想...
继续阅读 »
   临近新年,12月的第一天,自上次更新以来刚好一个月,环信Web IM V1.4.5已经发布,是不是很准时呢?毕竟想让环信小伙伴们都过上好年。环信Web IM V1.4.5版本最大的改动是增加了实时视频聊天功能,即在网页中打开就可以直接与APP视频通话,想想都觉得炫酷,赶紧下载体验吧!(需要注意:视频聊天需要https + Webkit浏览器哦)
   

e3355a0896eb7cdf34bc6859ca02e9f0.jpg


新功能:


1.GNU风格的版本号命名格式: 主版本号.子版本号.修正版本号 (新版本规则的1.4.5 = 旧版本规则的1.1.4.5)

2.【DEMO】好友之间可以通过webrtc进行视频聊(仅支持 https + Webkit浏览器)
 
3.【DEMO】支持同一账号最多8个标签页登录 `isMultiLoginSessions:true`
 
4.【DEMO】http访问加入ip策略功能,防止DNS劫持 `isHttpDNS:true`
 
5.【DEMO】新增两种安装引用方式(具体引用方式,请参考集成方式)

  • 添加 `<script>` 标签,并通过WebIM命名空间访问websdk
  • NPM(websdk 已经发布到NPM),先require,再访问WebIM



Bug修复:


1.【SDK】 解散群组不更新UI
 
2.【SDK】 修复了发送cmd消息成功后无法调用回调函数的bug


webim体验:https://webim.easemob.com/

版本历史:更新日志  
 
SDK下载:点击下载
收起阅读 »

Ping++ 「变现时代」大会

大会介绍 「变」意味着「变化」,「现」代表着「当下」。所谓「与时迁移,应物变化,设策之机也」,企业们不仅要活在当下,更要拥抱变化。 值此年关,Ping++ 携手各界合作伙伴,邀请到那些勇于自我颠覆、创新商业模式的企业家们,分享他们这一年来对于商业模式、...
继续阅读 »
大会介绍
「变」意味着「变化」,「现」代表着「当下」。所谓「与时迁移,应物变化,设策之机也」,企业们不仅要活在当下,更要拥抱变化。

值此年关,Ping++ 携手各界合作伙伴,邀请到那些勇于自我颠覆、创新商业模式的企业家们,分享他们这一年来对于商业模式、企业转型、业务增长上的见解与心得。

为了简单温暖的支付体验,Ping++ 惟愿伴你一路同行。

大会议程
时间:2016 年 12 月 17 日 13:00 - 17:00
地址:北京市朝阳区太阳宫北街 2 号院 1 号楼, LAVIN 玫瑰里·太阳公元·心之芳庭
(地铁乘坐城铁10号线、13号线到芍药居站下车即到)

13:00 - 13:30  入场签到
13:30 - 13:40  开场致辞
13:40 - 14:10  分主题 · 流量 | 在行 & 分答联合创始人 杨璐
14:15 - 14:45  分主题 · 内容 | 什么值得买 CEO 那昕
14:50 - 15:20  分主题 · 平台 | 掌合天下市场 VP 张龙飞
15:25 - 15:55  分主题 · 共享 | ofo 共享单车联合创始人 张巳丁
16:00 - 16:30  分主题 · 开放 | Ping++ 聚合支付 CEO 金亦冶
16:30 - 17:00  茶歇 & 交流

注:以上议程有可能变动,以现场实际议程为准
 
嘉宾介绍

杨璐.png


杨璐
在行&分答联合创始人,市场 VP
嘉宾简介:
在进入在行&分答前,杨璐拥有 12 年的广告从业经验。八年的奥美工作经验,每年服务过亿投放量的客户,练就了扎实的专业基础和团队管理能力,成为奥美亚太地区最年轻的客户群总监。加入在行后,与团队上线了 2016 年互联网界最吸引人瞩目的付费语音问答产品——分答。分答上线后 42 天成功融资 2500 万美金,估值上亿美金。

那昕.png


那昕
什么值得买 CEO
嘉宾简介:
曾任京东智能集团副总裁并推出京东硬件平台“JD+”计划,于 2015 年 6 月受创始人隋国栋邀请加入什么值得买。什么值得买(SMZDM.COM)成立于 2010 年 6 月 30 日,是一家集导购、媒体、工具、社区属性为一体的消费领域门户型网站,因其中立、专业而在众多网友中树立了良好口碑,为消费者提供决策建议。

张龙飞.png


张龙飞
掌合天下市场 VP
嘉宾简介:
资深互联网/电子商务从业者,曾先后服务过 263 网络集团、中国数码集团、学大教育集团以及国内最大的电子商务新闻门户亿邦动力网,熟悉电子商务行业、精通互联网市场营销。拥有 10 年的互联网行业工作经验,现任掌合天下市场营销中心负责人。掌合天下成立于 2013 年,是一家专注于快消品供应链的电子商务平台,依托独创的“城市合伙人”模式和专业的快消互联网运营经验,为供货商和超市提供便捷,透明,安全的一站式 B2B 综合服务,现业务已覆盖全国的 27 省及自治区的 600 多个城市,并于今年 10 月完成 7 亿元的 B 轮融资。

张巳丁.png


张巳丁
ofo 共享单车联合创始人
嘉宾简介:
重度骑行爱好者和极限运动爱好者。北大考古 2013 届本科,2015 届文化遗产硕士。ofo 是全球首个无桩共享单车出行解决方案,首创“单车共享”模式。上线一年来,ofo 已成为国内规模最大的共享单车平台,拥有 10 万辆共享单车,提供超过 2000 万次共享单车服务,为 21 座城市 200 多万用户提供便捷的出行服务。2016 年 10 月,ofo 完成 1.3 亿美元 C 轮融资。

张一甲.png


张一甲
Xtecher 联合创始人
嘉宾简介:
科技媒体、科技企业服务平台 Xtecher 联合创始人。毕业于北京大学数学科学学院,获经济学双学位。曾获中国数学奥林匹克金牌,入选国家集训队。毕业后先后就职于奥美中国、百度,为奔驰、伊利、奥迪、雀巢、康师傅、361度等国际一流客户提供品牌策划、创意管理和长期服务。Xtecher 成立于 2015 年 4 月,致力于发现最有潜力的早期科技项目,并为它们进行全面的价值加速。

金亦冶.png


金亦冶
Ping++ 创始人&CEO
嘉宾简介:
连续创业者,斯坦福大学电子工程硕士学位,2015 年入选福布斯中国 30 位 30 岁以下创业者榜单。2014 年创立面向开发者提供移动支付 SDK 服务的公司 --「Ping++」,让「7 行代码接入支付」成为了业内新标准,2015 年 12 月获得宽带资本领投的千万美元 B 轮融资。


大会媒体/合作联系:马锐 18608948398(同微信)、安贤龙 17710330365(同微信)

合作伙伴
主办方:Ping++ 聚合支付系统
联合主办:Xtecher
协办方:什么值得买、分答 & 在行、掌合天下、ofo 共享单车
特别合作:微软加速器、环信、北京天津企业商会、天天投、互动吧
合作伙伴:灵伴科技、纳什空间、鸟哥笔记、SDK.cn
媒体合作:Donews、创业邦、人人都是产品经理
速记支持:有道云笔记

合作伙伴.jpeg


ping++微信公众号,

pingxx.png


特别提醒:为保证活动质量,本次大会为收费制,50 元/人。环信作为本次大会的合作伙伴,其用户/粉丝可直接使用 VIP 邀请码进行报名,无需付费。请通过关注 Ping++ 公众号:Pingplusplus,后台回复”环信+公司+职位“领取 VIP 邀请码。

报名方式:扫描以下二维码进入报名页面,

报名.jpeg


输入邀请码,报名费会自动变为 0.01 元,支付 0.01 元后即可完成报名。 收起阅读 »

iOS 环信昵称、头像、群头像、群昵称处理

接着上次的文章来:iOS 新手集成单聊、群聊、语音和视频通话的简述 废话不多说,直接进入主题。因为这次不涉及sdk内的什么,所以不用管SDK版本。   重中之重:先和你的好基佬,安卓哥们定好走什么流程 环信官方不会跟你存储好友关系什么的,昵称头像什...
继续阅读 »
接着上次的文章来:iOS 新手集成单聊、群聊、语音和视频通话的简述

废话不多说,直接进入主题。因为这次不涉及sdk内的什么,所以不用管SDK版本。
 
重中之重:先和你的好基佬,安卓哥们定好走什么流程
环信官方不会跟你存储好友关系什么的,昵称头像什么的就不要想着偷懒了,所以自力更生吧!!


001.jpg


单人昵称 + 头像 

方法一:自家服务器建张表,把所有的好友关系存储起来,这也是最好的,能够做到昵称头像实时更新等:
1、后台建表存储好友关系后,我们在每次登陆后,开一个线程把表数据请求下来存起来。

2、写几个方法,根据环信ID查找表中的昵称和头像。

3、更新昵称和头像,可以像微信一样,点击头像查看好友详情时进行更新;还有自家好友列表应该会给最新的,这个时候同步更新一份。(写方法,进行更新)
方法二:把昵称头像放到消息拓展中。​



002.png


easeui中发送方法中把昵称头像传到ext中去




003.png


在push到聊天界面时,把昵称头像放到拓展中互传


原谅这是以前的代码,没有做到简化,原理一样,就是在聊天时,把自己的昵称头像和对方的昵称头像放到一个字典里,自己定Key,把它加到消息体中的拓展里面,每次拿到消息时,直接取消息体中的拓展,把相对应的拿出来展示就可以。
两者比较:
前者:最理想的方法之一,就是要多写点代码,建张表和刷选而已,具体缺点,我还没发现

后者:简单容易处理,新生版本可以考虑,减少开发时间,但是测试肯定给你找问题,我先聊几句,然后改个昵称和头像,尼玛,改完之前还是老昵称和头像,这是因为这些消息体也是存到本地的,没有给你更新,因为拓展就是为了便于开发者开发各种消息的,所以做不到给你更新。
重点:后者其实也可以跟新的啦!!!!​



004.png


这只是一个例子,我用来更新我的业务逻辑的


根据环信ID,拿到消息体的拓展,把以前的昵称和头像都更新下就可以啦!
具体的方法可以参考环信官方:http://docs.easemob.com/im/490integrationcases/10nickname
群聊名称 + 头像

方法一:把所有的好友信息包括昵称头像等放到群组的群名称中去。



005.png


群名称


拿到群名称后,自己写方法,把里面的的各种数据拿出来



006.png


各种方法




007.png


群名称的方法


其他的我就不做过多展示,给一个效果图:
群头像的九宫格展示:直接去gitub上面搜索就可以找到,各种各样的,只需把头像传进去,返回一个imageView(大部分是本地图片效果,所以需要自己改成网络的)。
方法二:拓展消息

同样可以借鉴单聊中使用到的消息体中的拓展。然后自己写方法把里面的数据拿出来,具体方法我没有操作过,有兴趣的朋友可以尝试下。

有建议和好的想法的小伙伴可以提出来,大家互相讨论下,增长知识。有不懂的可以找我:qq:1804094055   

009.jpg


  收起阅读 »

iOS 新手集成单聊、群聊、语音和视频通话的简述

最近项目中有涉及到即时通讯的功能,所用到的是环信3.1.5的sdk。下面就开始介绍其过程: (1)集成环信SDK: pod:pod 'HyphenateFullSDK' 手动:因为我们要使用到语音和视频的功能,所以我们需要导入(环信 iOS ...
继续阅读 »
最近项目中有涉及到即时通讯的功能,所用到的是环信3.1.5的sdk。下面就开始介绍其过程:

(1)集成环信SDK:

pod:pod 'HyphenateFullSDK'

手动:因为我们要使用到语音和视频的功能,所以我们需要导入(环信 iOS HyphenateFullSDK 开发使用(包含实时通话功能))HyphenateFullSDK这个包。手动导入包文件后,我们需要手动加上一些库:

第 1 步:SDK 包含实时语音依赖库有:

CoreMedia.framework

AudioToolbox.framework

AVFoundation.framework

MobileCoreServices.framework

ImageIO.framework

libc++.dylib

libz.dylib

libstdc++.6.0.9.dylib

libsqlite3.dylib

libiconv.dylib

(如果使用的是 xcode7,后缀为 tbd。)

第 2 步:SDK 不支持 bitcode,向 Build Settings → Linking → Enable Bitcode 中设置 NO。

(2)添加EaseUI:

必须和SDK相对应的版本,不然会出现各种报错(方法找不到或者不对)


注意:如果编译报错,1、先检查EaseUI中的第三方是否和本地工程中的重复。                                          2、在PCH文件中引入头文件时:
#ifdef __OBJC__                                                                                                                                     #import "easeUI.h"                                                                                                       #import "EMSDKFull.h"    等等                                                                                          #endif



正式开始代码的编写:

初始化SDK:


//AppKey:注册的AppKey,详细见下面注释。//apnsCertName:推送证书名(不需要加后缀),详细见下面注释。
EMOptions*options=[EMOptions optionsWithAppkey:@"douser#istore"];    options.apnsCertName=@"istore_dev";                                                              [[EMClient sharedClient]initializeSDKWithOptions:options];



注册:


EMError*error=[[EMClient sharedClient]registerWithUsername:@"8001"password:@"111111"];if(error==nil){NSLog(@"注册成功");}




登陆:


EMError*error=[[EMClient sharedClient]loginWithUsername:@"8001"password:@"111111"];if(!error){NSLog(@"登录成功");}


 


注意:具体的自动登录(免登陆)、重连、退出登陆、异地登录等请移驾至环信官方:http://docs.easemob.com/im/300iosclientintegration/30iossdkbasic



聊天会话列表界面:建议自己搭建,便于管理和拓展

1:直接用环信的列表界面:ConversationListController   直接初始化加到自己的tabar上去

2:自己搭建列表界面:

QQ20161202-0@2x.png


获取与自己相关的所有的会话


QQ20161202-1@2x.png


cell上的角标、时间、信息等


QQ20161202-2@2x.png


tabar上的角标


- (void)didReceiveMessages:(NSArray *)aMessages;是刷新该列表的重点(记住注册及代理)。对方发来的消息都会在这个方法中进行刷新列表及跟新角标数等。


注意:其他代码自行到demo中去寻找,环信demo中都有参考;
 
单聊+群聊:因为自己写的效果远没有环信的好,加上需求不要对聊天界面进行自定义,所以最后我还是直接用的demo中的界面(把demo中的ChatViewController拖到自己工程中,编译,注释掉报错的代码),初始化,push到聊天界面即可:


ChatViewController *chatController = [[ChatViewController alloc] initWithConversationChatter:conversation.conversationId  conversationType:conversation.type];


群列表:获取与之相关所有的群


NSArray *groups = [[EMClient sharedClient].groupManager getMyGroupsFromServerWithError:nil];


语音和视频及时通话:因为都是环信写的 所以把有关及时通话的文件拖进来;


ChatDemoHelper        CallViewController

1:在pct中加上 #define DEMO_CALL 1 

2:全局搜索#if DEMO_CALL == 1   删掉,编译如有报错请仔细看是不是哪句代码重复了或者哪里没有注释完全。

3.ChatDemoHelper.m中 注册代码到——#pragma mark - EMCallManagerDelegate宏中间的代码都可以注释掉



重中之重:在根控制器中一定要把控制器赋给ChatDemoHelper:[ChatDemoHelper shareHelper].mainVC = self;




QQ20161202-3@2x.png


语音和视频通话必走的代码


如果集成语音和视频过程中有问题的,可以参考官方给出的视频,不过只能借鉴:http://www.imgeek.org/video/24


 
上面所述就是我使用环信的心得,不是很全面。大神手下留情,新手一枚。后续我会把单人昵称、头像;群组名称、群组头像等等的一些方法写出来,如果发现我所提出的有问题和有好的建议,请直接提出来,也可以一起讨论。qq:1804094055 有问题可以找我,环信讨论群:340452063。


3899794-22ee296194d0ec7d.gif


 
 
  收起阅读 »

呼叫中心还是客户中心?

 近年来,呼叫中心常常被称为这个时代的“白领血汗工厂”。作为一名资深的客户服务代表、主管、经理、总监,当我第一次听到这种说法时,被深深地触动了。我熟悉的很多呼叫中心都把办公场所设在比较偏远的地方,在这些地方一般人很难把工作有效地完成做好,但客服工作人员却可以在...
继续阅读 »


57c8fe5332be6_1024.jpg



 近年来,呼叫中心常常被称为这个时代的“白领血汗工厂”。作为一名资深的客户服务代表、主管、经理、总监,当我第一次听到这种说法时,被深深地触动了。我熟悉的很多呼叫中心都把办公场所设在比较偏远的地方,在这些地方一般人很难把工作有效地完成做好,但客服工作人员却可以在电话里处理好各种任务。

想一想,传统的呼叫中心是什么样子呢?它们通常是由一些普通格子间组成,几乎没有什么可以发挥创造力的空间。通话时长和通话处理时长都是很重要的指标,这说明每一通电话都是在处理业务而不是单纯地交流互动。另外对于呼叫中心的工作人员来说,即使公司朝着蓬勃的方向发展,客服工作人员的发展空间也是极其有限的。

将呼叫中心转型为客户中心。有人会认为,以这样一种方式来加深企业与客户之间的关系,无异于痴人说梦。但事实真的是这样吗?其实只要做出一些调整,呼叫中心就可以变为客户中心。

第一步也是最简单的一步是将部门名称改为客户中心。如果我们希望员工成为客户关系的建立者,那么我们应该从部门的定位开始。在这个以交易时间、任务量和呼入量来微观管理的时代,我们应该时常这样反问自己:如果我们不以最好的方式来对待我们的员工,又怎能期望他们以最好的方式来对待我们最尊贵的客人?如果做不到最好,起码得一视同仁,只可惜我们连这样也做不到,我们只是一味地强调员工是企业与客户关系的建立者以及公司产品的形象代表,仅此而已。通常情况下,呼叫中心是企业与客户仅有的互动方式。客户愿意花时间打电话给我们,是因为遇到了各种类型的问题,他们需要我们提供帮助。难道我们不想让员工在成为公司政策执行者的同时还是客户问题的解决者吗?这样不仅不会让客户的投诉不断升级,也能更好地帮旅客解决问题。
如何管理比较合适呢?怎样能在让公司在付出最小代价的同时能够给员工最大的权限去帮客户解决问题?这就需要我们花时间培养员工提前预判的能力并在此基础上出色地完成任务,这样也能在机会来临时给客户带去惊喜。这其中的关键是需要准备,我们不能单纯地期望我们的员工认识到这些东西,也不能在一个问题上只告诉他们一次就期待他们能坚持,更不能在没有被加强训练的情况下期待他们保持正确的工作习惯。

解决方案:创建一个世界一流的服务体系。在这样一个体系中,所有员工都能发现并及时指出不足,坚持同样的标准,在把握机会的同时不断变得更好。为了能让员工更好地处理每天发生的不同情况,我们需要花更多的时间在幕后管理他们。这允许我们监督客服代表的活动以确保团队中的每个成员不在微观管理下都能恪尽职守。这让我们的客服代表们不会压力过大、负担过重进而保持员工数量的稳定性,因为我们没有足够的人员来雇佣和培训,这对呼叫中心来说是一大难题。这听起来很棒,但我们如何把它完成好呢?我建议从以下两个步骤开始,但两者都将需要投入一定的时间和人力成本。

第一步是让你的团队一起参与创造你的客户体验周期,这包括与团队一起研究确定客户的接触点。一旦确定了这些接触点,你就可以借此去剖析每一个人,找到可能出错的地方(服务缺陷),接下来就知道每个人和每通电话里需要做什么(操作和体验标准),同时我们能通过哪些方式来取悦客户(利用机会超越机会)。

通过这个项目,你和你的一线团队将会大开眼界,并产生新的使命感。你的团队会重现活力并能很好地完成工作。虽然这是一个很棒的开始,你不能指望这样一个开始在没有加强巩固的基础上就能保持不断发展的势头。接下来我们该做第二步了。

第二步是解决日常会议。在你说“这在这里永远都行不通,因为……”之前(我知道你会这样说,因为我听过所有的借口,而且我自己曾经也这样说过),可以思考下金牌服务的典范:丽思卡尔顿酒店。他们每天都会举行一个他们称之为“站起来”的会议。福来鸡也一样。这些公司已经忽略每个员工不能每天都出席会议的事实,因为他们一天有多种轮班制。他们能做的是利用好平台专注他们的服务价值,讨论并解决问题,庆祝每天成功的案例。

结论:这个过程在成就一个伟大的团建活动的同时也会不断提升团队成员的自主性和增强他们的归属感。创建属于自己客户体验周期,并在日常活动中不断加强,这有助于给团队带去新的使命感,并让员工变成真正的客户关系的建设者。随着日常会议深入,活动并不会因时间推移而逐渐消失,而是会变成一种超越规范并深入人心的文化。

本文原载于《客户世界》2016年11月刊;作者Dave Murray,DiJulius集团高级客户体验顾问;译者皮晶晶,深圳航空营销委电子商务电话营销中心运营人员。 收起阅读 »

终于等到你|“好客之道-客户管理轻学院”领跑互联网轻学时代

老师,我想学习,没有时间怎么办?” “我入行客户中心行业10年了,想分享一些自己的管理经验,没有渠道什么办?” “现在有好多微课,微信公众平台视频学习,直播学习,不知道如何规划,选择什么渠道学习什么办?” “这是一个最好的时代,也是一个最坏的时代”。...
继续阅读 »
老师,我想学习,没有时间怎么办?”
“我入行客户中心行业10年了,想分享一些自己的管理经验,没有渠道什么办?”
“现在有好多微课,微信公众平台视频学习,直播学习,不知道如何规划,选择什么渠道学习什么办?”

“这是一个最好的时代,也是一个最坏的时代”。我们对学习的需求一直存在,但面对茫茫书海以及网上众多课程,如何筛选,如何迅速抓住重点,如何快速吸收成了最关键的问题。
  2016年11月30日,广州。借举办“2016全媒体客户中心管理论坛”的契机,客户世界机构今天正式宣布推出“好客之道”客户管理轻学院。

845478162620468799.png


  “轻学院”以促进客户管理从业人员的岗位培训与职业发展、提升全社会客户管理意识及客户服务水平为宗旨;系统梳理客户中心和电子商务行业的专业知识并深入研讨相关领域的管理热点话题。帮助本领域的从业者大幅削减高昂的培训费用及大量的时间成本,在互联网+的热潮中真正享受低廉和便捷的专业培训,与行业大咖和知名讲师零距离交流,全面接收行业前端资讯,深度探讨客户管理精髓。
  客户世界机构从2003年1月开始编辑出版《客户世界》月刊,至今已14年,累计发行超过160期;同时累计组织编撰出版行业图书超过100本,常年为业内超过千家企业提供咨询和培训服务。作为客户中心行业的旗帜媒体和管理智库,在特定领域沉淀了丰富的资源。

      客户世界机构同步于2004年创立了国内最大的客户管理人俱乐部“好客会”,以加强行业同仁之间的互动;通过举办年会、行业/主题论坛及读书沙龙、编辑聚餐、心理学及禅修活动、户外健身运动等方式推动会员交流;已举办各类线下会员活动超过两百场,其中包括了中国客户中心行业历史最久、水准最高、规模最大的行业会议——客户世界年度大会,年度行业评选——“金耳唛杯”中国最佳客户中心,好客会读书沙龙,中国客户中心马拉松赛等业界耳熟能详的品牌活动。

       近年来,通讯技术和社交媒体的发展促进了交互的便捷、多元和高效;纸媒和咨询等知识积累方式以及会议和培训等知识传播手段亟待突破和创新。消费者连接技术、接触技术和体验技术的不断创新也正深刻改变着以客户中心为代表的客户交互服务领域,大数据、万物互联、认知计算的深入发展大大扩展了本领域的产业边界, 数字化、体验化、共享经济化的时代特征使得整个服务产业的融合发展成为趋势。

       基于互动技术的发展以及受众行为偏好的改变,客户世界机构适时推出“好客会” 知识平台及o2o行业社区(www.iCustomer.com.cn)。旨在搭建一个服务业内互助互帮、优质资源整合的平台,让更多从业者健康快速成长、获益、并享受于服务工作之中。“好客会”社区将采用APP、视频直播、微信互动、社区交流等整合式的渠道开展有计划的内容整合传播以及持续的深度互动。透过新渠道的知识平台,传播知识、培养人才、推动行业健康发展。本次推出的“好客之道”客户管理轻学院是“好客会”知识平台的核心内容,将深度梳理客户中心与电子商务行业的资源,采用视频直播、图文直播、内容点播相整合的传播方式,以管理讲堂、主题论坛、访谈对话、特别策划等栏目定期播出的形式与行业受众见面。区隔于传统呼叫中心的封闭式培训,也区别于大型电商服务在平台内部的客服公开课,是针对客户中心和电子商务行业的管理者和一线从业者搭建的与各行业客户管理工作环节相关的深度学习平台,并为客户中心和电子商务从业者提供行业交流社群和活动组织平台。课程的组织将更加社群化和互联网化,以更加轻松的形式呈现。实名制会员将可以通过视频课程、影音课程、笔记分享等较轻的形式,随时随地进行学习和分享。课程内容围绕着帮助管理者明确管理战略、清晰管理战术、完善高效有序地运作团队,以及帮助一线从业者理解工作的意义、提高工作技能、打通职业晋升通道等。


        就在“好客会”平台正式上线发布前的一周(11月24日,感恩节),“好客之道”工作室在位于北京朝阳区兴隆公园的“好客咖啡馆”启动了节目录制工作。自2016年12月起,相关内容将陆续每周推出。 收起阅读 »

安卓修改头像样式

开始集成环信时候,也碰到了很多问题,然后就是求助了很多前辈,各种表哥,今天就给大家说一下Android如何修改头像样式,   集成环信个人感觉还很容易的,当然,坑也很多,环信很多东西都是可以自己定义的,直接进入正题吧,在demo的EaseUi里面utils包下...
继续阅读 »
开始集成环信时候,也碰到了很多问题,然后就是求助了很多前辈,各种表哥,今天就给大家说一下Android如何修改头像样式,
 
集成环信个人感觉还很容易的,当然,坑也很多,环信很多东西都是可以自己定义的,直接进入正题吧,在demo的EaseUi里面utils包下面有个EaseUserUtils类里面有如下代码:


SH34I~)J0_PEZ_@45GOO9KB.png


然后只要在setUserAvatar这个方法里面稍作修改

T3HR{@OQ5K7_EH6R3}0W(LU.png


这个GlideCircleTransform类   可以写成内部类,也可在是外部的,建议内部,GlideCircleTransform这种可以自己写,不会的去百度,实在不会的加下面的群,这个方法我把代码给你们贴上:

public static void setUserAvatar(Context context, String username, ImageView imageView){
EaseUser user = getUserInfo(username);
if(user != null && user.getAvatar() != null){
try {
int avatarResId = Integer.parseInt(user.getAvatar()); //Glide.with(context).load(avatarResId).into(imageView); Glide.with(context).load(avatarResId).transform(new GlideCircleTransform(context)).into(imageView);
} catch (Exception e) {
//use default avatar //Glide.with(context).load(user.getAvatar()).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.ease_default_avatar).into(imageView);
Glide.with(context).load(user.getAvatar()).diskCacheStrategy(DiskCacheStrategy.ALL). placeholder(R.drawable.ease_default_avatar).transform(new GlideCircleTransform(context)).into(imageView);
}
}else{
//Glide.with(context).load(R.drawable.ease_default_avatar).into(imageView); Glide.with(context).load(R.drawable.ease_default_avatar).transform(new GlideCircleTransform(context)).into(imageView);
}
}

自己去根据图片排版去吧,到这里已经完成一半了,前面说了环信的好,该吐槽一下坑了,我改到这里后,只有列表变成想要的样子,会话页面还是不行,后来和帝都的一个妹子交流了下,她给我说要把EaseUi里面关于聊天界面,会话列表,联系人列表,个人资料布局里面的图片资源全部删除就行了,具体几个我忘记了,大概有七个左右,是左右,我忘了,也没几个布局,你们可以一个一个找找,这样就可以修改成自己喜欢的头像样式了


2A@)A`D2V`UO~9WOO`QH]1Q.jpg


 
好了,到这里就差不多了,如果实在还是不行,或者是有其他问题的,请加环信互帮互助群 340452063 到群里找我 杭州-andorid-中草  龙虾头像  最后附上二表哥送的四句话
做一番一生引以为豪的事业
找一个一生荣辱与共的妻子
在有生之年报答帮过我的人
并且帮助那些需要帮助的人 收起阅读 »

环信助力爱宠医生为50万宠物主人在线解决疑难杂症

 爱宠医生是全国首款宠物医生在线问诊App,爱宠医生(http://www.5ichong.com/)目前注册用户超过50万,100+服务城市,3000+合作医院以及5000+认证宠物医生,致力于打造互联网宠物医疗第一品牌。宠物主人查找周边宠物医生进行实时在线...
继续阅读 »
 爱宠医生是全国首款宠物医生在线问诊App,爱宠医生(http://www.5ichong.com/)目前注册用户超过50万,100+服务城市,3000+合作医院以及5000+认证宠物医生,致力于打造互联网宠物医疗第一品牌。宠物主人查找周边宠物医生进行实时在线问诊,并通过问诊选择周边靠谱的宠物医院和宠物医生,同时宠物医生可以给医院招揽客源,管理自己的客户,形成一个宠物医生的CRM+宠物主人的问诊平台,是宠物界的“春雨医生”。

screen1136x1136.jpeg


1.遇到的挑战:

    爱宠医生技术总监张振亚表示:“由于业务主要为宠物主人的在线问诊平台,对于初创公司来讲,IM这一块由自己来开发不仅时间不允许,技术沉淀也不够,即便开发出来稳定性也很难保证。所以当时在相对于比较成熟的几款IM服务中,通过服务质量,稳定性,开发团队以及和业务需求进行匹配时,最终认为环信最适合我们的业务。”

2.环信解决方案:

    因为在业务场景上宠物主人与宠物医生的对话是以问题的形式出现的,解决问题后问题需要关闭。所以单聊的方式不是特别符合爱宠医生的需求,爱宠医生对聊天室与群组进行对比,最后决定使用群组的方式来实现,宠物主人提出问题创建群组将宠物医生加入群组(群组上线2人)进行对话,问题结束后删除群组,抢单模式为将第一位宠物医生移除群组后再有抢单医生抢单成功后在移入群组进行对话。

3.价值体现:

    为超过50万的宠物主人在线解决平常遇到的疑难杂症,环信即时通讯云在当中起到了至关重要的管道作用,保证了爱宠医生IM服务7×24的正常稳定运行。

    爱宠医生技术总监张振亚表示:整体来讲,对环信的服务很满意。在创业初期就帮助我们解决了技术集成等问题,并且通过不断的版本迭代一直稳定运行到现在。技术支持服务也相对的很及时也很到位,绝大多数的问题可以在第一时间帮助我们解答。版本迭代更新也是很稳定的,也比较开放,可以让我们在使用过程中自定义与业务相关的功能。正是因为环信对于我们业务上的帮助,可以让我们在越来越繁华的宠物行业以及宠物医疗行业占得先机。

 关于爱宠医生

宠物生病了怎么办?不用马上去医院,使用爱宠医生APP随时随地向周边的宠物医生和全国知名的宠物专家提问,给宠物问诊治疗,你还可以查到小区附近的宠物医院,查看宠物医院的评论,帮你找到最好、最靠谱的宠物医院和宠物医生。

640.jpg


爱宠医生是上海宠零宠网络科技有限公司旗下互联网宠物医疗平台,公司成立于2015年6月,坐落于上海浦东张江高科技园区,是浦东软件园孵化器孵化企业。公司于2015年和2016年分别获得中金资本和普华资本的两轮融资。公司核心管理团队均为原互联网上市公司核心人员和宠物行业资深人士。 公司旗下产品包括宠物医生免费在线问诊app、宠物医院智能SaaS管理系统以及宠物医院的供应链采销平台。拥有注册宠物主50多万人,业务覆盖北上广等100多个城市,3000多家宠物医院,5000多位宠物医生,已成为当前互联网宠物医疗第一品牌。公司在 北京举办的“2016创新中国春季峰会”,获得优秀企业奖,同时,在2016年香港举办的“亚洲智能手机应用程式大赛”中获得优秀项目奖。 收起阅读 »

【环信3.x Android加表情】加表情三部曲,你值得拥有!!!

写在前面:不知道是不是因为这个需求太简单了还是什么原因,我发现这个需求的教程居然少的可怜!!!这让我这种小白如何是好~!!!正好今天实现了,就写出来,希望能帮助到和我这类的小白。   首先我实现方式就是仿照官方demo里兔斯基的实现方式,在自带的表情基础上增加...
继续阅读 »
写在前面:不知道是不是因为这个需求太简单了还是什么原因,我发现这个需求的教程居然少的可怜!!!这让我这种小白如何是好~!!!正好今天实现了,就写出来,希望能帮助到和我这类的小白。
 
首先我实现方式就是仿照官方demo里兔斯基的实现方式,在自带的表情基础上增加一组新表情。所以就很简单了!!!首先就不说如何集成环信demo了,相信有这个需求的人都是已经集成好了的。
  1. 第一部

    首先找到EmojiconExampleGroupData这个类,复制一份照着修改下,我的代码如下:
package com.liancheng.tiantianzhengchong.HuanXin.domain;


import com.hyphenate.easeui.domain.EaseEmojicon;
import com.hyphenate.easeui.domain.EaseEmojicon.Type;
import com.hyphenate.easeui.domain.EaseEmojiconGroupEntity;
import com.liancheng.tiantianzhengchong.R;

import java.util.Arrays;

public class EmojiconSinaGroupData {

private static int[] icons = new int[]{
R.drawable.d_aini,
R.drawable.d_aoteman,
R.drawable.d_baibai,
R.drawable.d_baobao,
R.drawable.d_beishang,
R.drawable.d_bishi,
R.drawable.d_bizui,
R.drawable.d_chanzui,
R.drawable.d_chijing,
R.drawable.d_dahaqi,
R.drawable.d_dalian,
R.drawable.d_ding,
R.drawable.d_doge,
R.drawable.d_erha,
R.drawable.d_feizao,
R.drawable.d_ganmao,
R.drawable.d_guzhang,
R.drawable.d_haha,
R.drawable.d_haixiu,
R.drawable.d_han,
R.drawable.d_hehe,
R.drawable.d_heixian,
R.drawable.d_heng,
R.drawable.d_huaixiao,
R.drawable.d_huaxin,
R.drawable.d_jiyan,
R.drawable.d_keai,
R.drawable.d_kelian,
R.drawable.d_ku,
R.drawable.d_kulou,
R.drawable.d_kun,
R.drawable.d_landelini,
R.drawable.d_lang,
R.drawable.d_lei,
R.drawable.d_miao,
R.drawable.d_nanhaier,
R.drawable.d_nu,
R.drawable.d_numa,
R.drawable.d_nvhaier,
R.drawable.d_qian,
R.drawable.d_qinqin,
R.drawable.d_shayan,
R.drawable.d_shengbing,
R.drawable.d_shenshou,
R.drawable.d_shiwang,
R.drawable.d_shuai,
R.drawable.d_shuijiao,
R.drawable.d_sikao,
R.drawable.d_taikaixin,
R.drawable.d_tanshou,
R.drawable.d_tian,
R.drawable.d_touxiao,
R.drawable.d_tu,
R.drawable.d_tuzi,
R.drawable.d_wabishi,
R.drawable.d_weiqu,
R.drawable.d_wu,
R.drawable.d_xiaoku,
R.drawable.d_xiongmao,
R.drawable.d_xixi,
R.drawable.d_xu,
R.drawable.d_yinxian,
R.drawable.d_yiwen,
R.drawable.d_youhengheng,
R.drawable.d_yun,
R.drawable.d_zhuakuang,
R.drawable.d_zhutou,
R.drawable.d_zuiyou,
R.drawable.d_zuohengheng,

R.drawable.f_geili,
R.drawable.f_hufen,
R.drawable.f_jiong,
R.drawable.f_meng,
R.drawable.f_shenma,
R.drawable.f_v5,
R.drawable.f_xi,
R.drawable.f_zhi,
R.drawable.h_buyao,
R.drawable.h_good,
R.drawable.h_haha,
R.drawable.h_jiayou,
R.drawable.h_lai,
R.drawable.h_ok,
R.drawable.h_quantou,
R.drawable.h_ruo,
R.drawable.h_woshou,
R.drawable.h_ye,
R.drawable.h_zan,
R.drawable.h_zuoyi,
R.drawable.l_shangxin,
R.drawable.l_xin,
R.drawable.lxh_haoaio,
R.drawable.lxh_haoxihuan,
R.drawable.lxh_oye,
R.drawable.lxh_qiuguanzhu,
R.drawable.lxh_toule,
R.drawable.lxh_xiaohaha,
R.drawable.lxh_xiudada,
R.drawable.lxh_zana,
R.drawable.o_dangao,
R.drawable.o_feiji,
R.drawable.o_ganbei,
R.drawable.o_huatong,
R.drawable.o_lazhu,
R.drawable.o_liwu,
R.drawable.o_lvsidai,
R.drawable.o_weibo,
R.drawable.o_weiguan,
R.drawable.o_yinyue,
R.drawable.o_zhaoxiangji,
R.drawable.o_zhong,
R.drawable.w_fuyun,
R.drawable.w_shachenbao,
R.drawable.w_taiyang,
R.drawable.w_weifeng,
R.drawable.w_xianhua,
R.drawable.w_xiayu,
R.drawable.w_yueliang

};

private static String[] icons_name = new String[]{
"[爱你]",
"[奥特曼]",
"[拜拜]",
"[抱抱]",
"[悲伤]",
"[鄙视]",
"[闭嘴]",
"[馋嘴]",
"[吃惊]",
"[打哈气]",
"[打脸]",
"[顶]",
"[doge]",
"[二哈]",
"[肥皂]",
"[感冒]",
"[鼓掌]",
"[哈哈]",
"[害羞]",
"[汗]",
"[呵呵]",
"[黑线]",
"[哼]",
"[坏笑]",
"[花心]",
"[挤眼]",
"[可爱]",
"[可怜]",
"[酷]",
"[骷髅]",
"[困]",
"[懒得理你]",
"[浪]",
"[泪]",
"[喵喵]",
"[男孩儿]",
"[怒]",
"[怒骂]",
"[女孩儿]",
"[钱]",
"[亲亲]",
"[傻眼]",
"[生病]",
"[草泥马]",
"[失望]",
"[衰]",
"[睡觉]",
"[思考]",
"[太开心]",
"[摊手]",
"[舔屏]",
"[偷笑]",
"[吐]",
"[兔子]",
"[挖鼻屎]",
"[委屈]",
"[污]",
"[笑cry]",
"[熊猫]",
"[嘻嘻]",
"[嘘]",
"[阴险]",
"[疑问]",
"[右哼哼]",
"[晕]",
"[抓狂]",
"[猪头]",
"[最右]",
"[左哼哼]",

"[给力]",
"[互粉]",
"[囧]",
"[萌]",
"[神马]",
"[威武]",
"[喜]",
"[织毛线]",
"[NO]",
"[good]",
"[haha]",
"[加油]",
"[来]",
"[ok]",
"[拳头]",
"[弱]",
"[握手]",
"[耶]",
"[赞]",
"[作揖]",
"[伤心]",
"[心]",
"[好爱哦]",
"[好喜欢]",
"[噢耶]",
"[求关注]",
"[偷乐]",
"[笑哈哈]",
"[羞嗒嗒]",
"[赞啊]",
"[蛋糕]",
"[飞机]",
"[干杯]",
"[话筒]",
"[蜡烛]",
"[礼物]",
"[绿丝带]",
"[围脖]",
"[围观]",
"[音乐]",
"[照相机]",
"[钟]",
"[浮云]",
"[沙尘暴]",
"[太阳]",
"[微风]",
"[鲜花]",
"[下雨]",
"[月亮]"
};


private static final EaseEmojiconGroupEntity DATA = createData();

private static EaseEmojiconGroupEntity createData() {
EaseEmojiconGroupEntity emojiconGroupEntity = new EaseEmojiconGroupEntity();
EaseEmojicon[] datas = new EaseEmojicon[icons.length];
for (int i = 0; i < icons.length; i++) {
datas[i] = new EaseEmojicon(icons[i], icons_name[i], Type.BIG_EXPRESSION);
datas[i].setBigIcon(icons[i]);
//you can replace this to any you want
datas[i].setName(icons_name[i]);
datas[i].setIdentityCode("sina" + (1000 + i + 1));
}
emojiconGroupEntity.setEmojiconList(Arrays.asList(datas));
emojiconGroupEntity.setIcon(R.drawable.ee_3);
emojiconGroupEntity.setType(Type.BIG_EXPRESSION);//设置类型,如果是nomal就可以输入输入框
return emojiconGroupEntity;
}


public static EaseEmojiconGroupEntity getData() {
return DATA;
}
}
这里说明下,
datas[i] = new EaseEmojicon(icons[i], icons_name[i], Type.BIG_EXPRESSION);

emojiconGroupEntity.setType(Type.BIG_EXPRESSION);

主要是这里要设置下type类型,这里以BIG_EXPRESSION形式,如何设置成nomal的话,发出来的是纯文字的,不能显示表情的,需要用的话,得修改easeui里的东西,不推荐。一般需要加新表情都是已大图形式的,如果需要纯文字,可以加在默认的(第一组)表情里!
  2.第二部
   找到ChatFragment,找到((EaseEmojiconMenu)inputMenu.getEmojiconMenu()).addEmojiconGroup(EmojiconExampleGroupData.getData());
复制一份,修改成
((EaseEmojiconMenu) inputMenu.getEmojiconMenu()).addEmojiconGroup(EmojiconSinaGroupData.getData());
加在这句后面。
  3.第三部
   找到DemoHelper,找到easeUI.setEmojiconInfoProvider,写成如下:
  //set emoji icon provider
easeUI.setEmojiconInfoProvider(new EaseEmojiconInfoProvider() {

@Override
public EaseEmojicon getEmojiconInfo(String emojiconIdentityCode) {
//第一种表情
EaseEmojiconGroupEntity data = EmojiconExampleGroupData.getData();
for (EaseEmojicon emojicon : data.getEmojiconList()) {
if (emojicon.getIdentityCode().equals(emojiconIdentityCode)) {
return emojicon;
}
}
//新增的第二种表情
EaseEmojiconGroupEntity data_sina = EmojiconSinaGroupData.getData();
for (EaseEmojicon emojicon : data_sina.getEmojiconList()) {
if (emojicon.getIdentityCode().equals(emojiconIdentityCode)) {
return emojicon;
}
}
return null;
}

@Override
public Map<String, Object> getTextEmojiconMapping() {
return null;
}
});
   最后写完了,希望能帮到和我这样的小白!!!
 
  收起阅读 »

移动APP测试中的最佳做法!

一说起软件测试,测试员想到肯定是去检查文件,功能,API,性能并确定软件是否安全,以及关于软件特定部分的其他事项。但是对于移动测试,测试员不得不基于用户移动使用模式考虑移动相关的功能。   功能测试   每项开发的新功能都需要进行测试。移动app测试中功能测试...
继续阅读 »
一说起软件测试,测试员想到肯定是去检查文件,功能,API,性能并确定软件是否安全,以及关于软件特定部分的其他事项。但是对于移动测试,测试员不得不基于用户移动使用模式考虑移动相关的功能。
 
功能测试
 
每项开发的新功能都需要进行测试。移动app测试中功能测试是一个重要方面,移动测试员应该要进行手动测试和自动化测试。刚开始测试时,测试员必须把移动app当做"黑盒"一样进行手动测试,看看提供的功能是否正确并如设计的一样正常运作。除了经典软件测试,像点击按钮看看会发生什么,测试员还必须执行更多功能的移动设备专门的测试。
 
如今,现代移动设备都有触摸屏,要求多点触控动作来与它们互动。设备可以是纵向或横向显示屏。它们提供动作,倾斜和螺旋传感器。它们有不同的接口可以连接其他设备或服务,比如GPS,NFC,照相机,LED等等。
 
移动软件测试员必须确保app的所有特定设备功能在app里都能用。移动设备的种类这么多,测试时要将所有的覆盖是不可能的,所以功能测试时测试员要专注于他们app的关键之处。什么是真的简单有效的呢?设备旋转。我测试工作期间发现有许多bug仅需将设备从纵向旋转为横向再旋转回来就好了。

 

除了整个手动测试过程,测试自动化对移动app也很重要。每个代码变化或新功能都可能影响现存功能及它们的状态。通常手动回归测试时间不够,所以测试员不得不找一个工具去进行自动化回归测试。现在市面上有很多移动测试自动化工具,根据我的经验,真机测试效果是最好的,我自己常用的TestBird测试工具,用来进行APP的功能测试,当然,根据开发策略和结构,质量保证专家需要找出最适合他们环境的自动化工具。
 
选择一个工具对测试自动化并不容易,但做决定时有一点要牢记,因为很重要:测试自动化应该使用同样的编程语言作为产品代码。如果测试和产品代码用一样的语言去写,那对测试员和开发员都有好处,因为这就使得他们做配对代码时可以轻松些。测试员可以和开发员在同一水平进行交流,他们可以执行测试和产品代码的代码审查。对于测试自动化,开发员可以用他们习惯的语言编写他们自己的脚本。
 
总结:
·         把app作为"黑盒"进行测试并试着中断它。

·         打开移动app的每个屏幕并将设备从纵屏变为横屏再变回纵屏。

·         别忘了去测试设备特定的功能,比如传感器和通信接口。

·         为移动app编写测试自动化脚本。

·         选择一个适应公司策略和结构的测试自动化工具。

·         测试和产品代码应该用同一种语言。
 
非功能测试
 
移动app测试的另一重要方面是移动app的非功能需求。移动app在推出市场或进行进一步开发前,移动测试员有许多需要测试的问题。

 

早期开发阶段要进行的第一个测试应该是实用性测试。通常是由alpha用户或同事进行的。走进一家咖啡馆或餐厅,问问里面的人他们的app使用情况。让他们看看现阶段开发的第一个版本并收集反馈,看看用户是否能很好地使用新功能,以便得出第一印象。
 
检查app的性能。将推出的版本与当前版本做一番比较,看看性能是一样?更好?还是更差?将app安装到旧的设备上,看看该app在旧设备上是否仍能运作,无论硬件设备好或差。最先进的设备也一样要这么做。

 

测试电话,短信,彩信,微博或其他通知进来时app的反应。使用app时检查一下电量。确保测试过程测试设备是充满电的并每十分钟检查一下电池使用情况,看看该app有没有太耗电。在低电量时把app安装到设备上看看会发生什么。检查app的内存使用情况。如果app在本地文件系统中存储数据,测测不同内存卡的使用情况。想想看本地存储快满时会发生什么呢--app会崩溃或弹出出错提醒框来通知用户吗?
 
测试app的安装和删除过程。更重要的是,测试从老版本升级为新版本的过程。或许本地数据库已经改变了,这样就会引起一些严重的迁移问题。

 

App被本地化了吗?测试员需要用不同的语言测试app。记得在不同的网络载体上以不同的网速进行测试。确定该app在GPRS,EDGE,UMTS,LTE和WiFi环境下都能运作。
 
别忘了检查网络连接不好或完全掉了时app会怎么反应。飞行模式下使用该app看看如果一个请求失败了会发生什么。将测试设备连接到电脑上并检查开发日志文件有没有例外、警告或其他奇怪的异常之处。这些只是移动测试员和开发员开发和测试一个app时应该考虑的非功能需求中的一部分。每方面都检查到位是绝不可能的,因此整体团队应该支持QA成员尽量覆盖更多方面以防用户得到不好的体验。或者使用众测平台,我习惯性的用TestBird ,当然腾讯阿里也有类似的产品。

 

总结:
·         做实用性测试。

·         比较app已推出版本和新版本的性能。

·         检查电话,短信,彩信或微博或进来时app的反应。

·         检查测试设备的电量。

·         测试app的内存使用情况。

·         安装并删除app。

·         测试从旧版本升级到新版本的过程。

·         检查语言的转换。

·         在不同的载体和网络连接,如GPRS,WiFi,orLTE,环境中使用app。

·         检查日志文件的错误或例外。 收起阅读 »

李理:从Image Caption Generation理解深度学习(part II)

书接上文:环信李理:从Image Caption Generation了解深度学习(part I   2. 机器学习基本概念和前馈神经网络 2.1 机器学习基本概念    大家可能平时都写过很多程序,写程序和机器学习的思路可能有一些不同。写程序时,...
继续阅读 »
书接上文:环信李理:从Image Caption Generation了解深度学习(part I
 
2. 机器学习基本概念和前馈神经网络
2.1 机器学习基本概念

   大家可能平时都写过很多程序,写程序和机器学习的思路可能有一些不同。写程序时,我们是“上帝”,我们规定计算机的每一个步骤,第一步做什么第二步做什么,我们称之为算法。我们能够控制所有的情况,如果出了任何问题,肯定都是程序员的责任。而在机器学习的时候,我们只是“老师”。我们告诉学生(计算机)输入是什么,输出是什么,然后期望它能够学到和我们类似的知识。比如我们跟小孩说这是狗,那是猫,我们没有办法像上帝那样拿着“纳米手术刀”去操作人脑神 经元的连接方式。我们只能不断的给小孩“训练数据”,然后期望他能够学会什么是猫,即使我们觉得他“学会”了识别猫,我们也没有办法知道他是“怎么”学会 的,而且同样的训练过程可能换一个人就不好使。

   机器学习和人类的学习是类似的——我们也是给它训练数据,然后期望它能学会。我们会给机器建一个模型,从数学的角度来说一个模型就是一个函数,它的输入一般是一个向量【当然可以是二维的矩阵如图片或者三维的张量比如视频】,输出可以是有限的离散的标签如“猫”,“狗”,这类问题我们称之为分类;而如果输出 是连续的值比如用这个模型来预测气温,那么我们就称之为回归。其实人类的很多科学活动和日常生活,都是在“学习”模型和“应用”模型。比如开普勒通过观测 大量天文数据“归纳”出行星的运动规律。从本质上讲,智能就是从“过去”学习,然后根据“现在”来预测可能的将来并根据自己的目标选择有利于自己行为。只不过之前,似乎只有人类能够从数据中“学习”出规律,而人工智能的目标就是让机器也有类似的学习能力。

   模型用数学来说就是一个函数,我们人脑的函数由神经元的连接构成,它可能是一个很复杂的函数,我们现在还很难彻底研究清楚。神经网络就是试图通过计算机来 模拟和借鉴人脑这个模型,除了我们这里要讲的神经网络之外,机器学习领域还有各种各样的模型,它们各有特点。但不管形式怎么变化,本质都是一个函数。一个(或者更准确的是一种)模型一般都是一种函数形式,它有一些“参数”可以改变。而学习的过程就是不断调整这些参数,使得输出(尽量)接近“正确”的答案。 但是一般情况下很难所有的数据我们都能预测正确,所以一般我们会定义一个loss function,可以理解为“错误”的程度,错的越“离谱”,loss就越大。而我们的目标就是调整参数使得loss最小。

   但是我们是在“训练”数据上调整的参数,那么它能在“测试”数据上也表现的好吗?这个就是模型的“泛化”能力了。就和人在学校学习一样,有的同学做过的一 模一样的题就会,但是考试时稍微改变一下就不会了,这就是“泛化”能力太差,学到的不是最本质的东西。所以平时会定期有一些“模拟考试”,来检验学生是不 是真的学会了,如果考得不好,那就打回去重新训练模型调整参数。这在机器学习里对应的就是validation的阶段。最后到最终的考试了,就是最终检验 的时候了,这个试卷里的题目是不能提前让人看到的,只能拿出来用一次,否则就是作弊了。对应到机器学习里就是test阶段。

   当然这里用通俗的话描述了机器学习,主要是有监督的学习。其实机器学习还有无监督的学习和强化学习。前者就是不给答案,只给数据,让人总结规律;而后者会有答案,但是答案不是现在就告诉你。我个人觉得人类社会里更多的是监督学习和强化学习。从人类社会总体来说,强化学习是获取新知识的唯一途径,也就是向自 然学习,我们做了一个决策,其好坏可能要很长一段时间才能显现出来。而学习出来的这些知识通过监督的方式,通过家庭和学校的教育教给下一代。

   另外输出除了简单的分为离散和连续,还可以是序列(时序)的,比如自然语言(文本)是一个字符串的序列 ,对于我们的Image Caption Generation就是生成一个单词序列。另外还有更复杂的输出,比如parsing,输出是一棵语法树。

2.2 多层神经网络

   前面介绍了机器学习的基本概念,接下来我们就来学习一下神经网络。现在流行的说法“深度学习”,其实大多指的就是“深度神经网络”,那么首先我们先了解一下“浅度神经网络”,也就是传统的神经网络。这里的内容主要来自http://neuralnetworksanddeeplearning.com的前两章。

2.2.1 手写数字识别问题

   我们在学习一门新的语言时会写一个hello world程序,而mnist数据的手写数字识别就是一个很好的学习机器学习(包括深度学习)的一个hello world任务。

   计算机和人类大脑似乎有很大的不同,很多人类认为复杂的工作计算机可能认为很简单,而人类认为很简单的事情计算机可能非常难处理。比如数字的计算,记忆,人类的准确度和速度都远远不如计算机。但是识别0-9的手写数字,我们觉得很轻而易举的事情,让计算机程序来处理却异常困难。经过数百万年进化的人类视觉系统在我们大脑没有意识到的时候就已经帮我们完成了数字的识别,把那些复杂的视觉处理过程深深的掩藏了起来。但当我们想自己写一个程序来识别数字的时候,这些困难才能体现出来。首先,对于计算机来说,它“看到”的不是数字,甚至不是笔画。它“看到”的只是一个二位的矩阵(数组),每个点都是一个数字。比如下图,我们“看到”的是左边的“猫”,其实计算机“看到”的是右边的像素灰度值。当然我们视觉系统的视网膜看到的也是类似的一些“数值”,只不过我们的视觉系统已经处理了这些信息并且把它识别成了“猫”(甚至和语言还做了映射)。 

001.png


   MNIST数据介绍:MNIST的每个图片经过缩放和居中等预处理之后,大小是28*28,每个点都是0-255的灰度值,下图是一些样例。总共有60,000个训练数据(0-9共10个类别,每个类别6,000个)和10,000个测试数据。一般会拿60000个中的50000个来做训练集,而剩下的10000个用来做验证集(用来选择一些超参数)。

002.png


   如果我们自己来写一个“算法”识别数字“9”,我们可能会这么定义:9在上面有个圆圈,在这个圆圈的右下部分有一个竖直的笔画。说起来很简单,如果用算法 来实现就很麻烦了:什么是圆圈?每个人画的圆圈都不同,同样竖直的笔画怎么识别,圆圈和竖直笔画连接处怎么寻找,右下是哪?大家如果有兴趣可以尝试一下用 上面的方法,其实最早做数字识别就是这样的思路。

   机器学习的思路则不同,它不需要这么细节的“指示”计算机应该怎么做。而是给计算机足够的“训练”样本,让它“看”不同的10个数字,然后让它“学”出 来。前面我们也讲了,现在的机器学习一般是一个参数化的模型。比如最简单的一个线性模型:f(w;x)=w0+ w1*x1+w2*x2。如果我们的输入有两个“特征”x1和x2,那么这个模型有3个参数w0,w1和w2,机器学习的过程就是选择“最优”的参数。对 于上面的mnist数据,输入就是28*28=784维的向量。

   如果用“原始”的输入作为“特征”,线性的模型很可能学到一些简单的特征,比如它看到1一般是分布在从上到下居中的一些位置,那么对于这些位置一旦发现有比较大的灰度值,那么就倾向于判断成1。如果一个像素点2也经常出现,但3不出现,那么它就能学到如果这个像素出现,那么这个数字是2和3的可能性就大一些。

   但是这样的“特征”可能不是“本质”的,因为我写字的时候笔稍微平移一点,那么你之前“学到”的参数就可能有问题。而更“本质”的特征是什么呢?可能还是像之前我们总结的——9在上面有个圆圈,在这个圆圈的右下部分有一个竖直的笔画。我们把识别一个数字的问题转化成圆圈和竖直笔画的问题。传统的机器学习需要方法来提取“类似”(但不完全是)基本笔画这样的“特征”,这些特征相对于像素的特征会更加“本质”。但是要“提取”这些特征需要很多的“领域”知识,比如图像处理的技术。所以使用传统的机器学习方法来解决问题,我们不但需要很多机器学习的知识,而且也需要很多“领域”的知识,同时拥有这两方面的知识是比较难的。

   而“深度学习”最近之所以火热,其中很重要的一个原因就是对于很多问题,我们只需要输入最原始的信号,比如图片的像素值,通过“多层”的网络,让底层的网络学习出“底层”的特征,比如基本的形状,而中间的层学习出抽象一点的特征,比如眼睛鼻子耳朵。而更上的层次识别出这是一个猫还是一个狗。所有这些都是机器学习出来的,所以基本不需要领域的知识。

003.png


   上面的图就说明了这一点,而且我们发现越是底层的特征就越“通用”,不管是猫鼻子还是狗眼睛,可能用到的都是一些基本的形状,因此我们可以把这些知识(特征)transfer到别的任务,也就是transfer learning,后面我们讲到CNN的时候还会提及。

2.2.2 单个神经元和多层神经网络(MLP)

   神经网络从名字来看是和人类的大脑有些关系的,而且即使到现在,很多有用的东西如CNN和Attention,都有很多借鉴神经科学研究人脑的结果的。不过这里我就不介绍这些东西了,有兴趣的读者可以找一些资料来了解。

一个神经元如下图的结构:

004.png


   它的输入是一个向量,(x1,x2,x3),输出是一个标量,一个实数。z=w0+ w1*x1 + w2*x2 + w3*x3。z是输入的加权累加,权值是w1,w2,w3,w0是bias,输出 output = f(z)。函数f一般叫做激活函数。最早流行的激活函数是Sigmoid函数,当然现在更流行Relu和它的改进版本。Sigmoid函数的公式和图形如下:

005.png




006.png


   当z=0时,sigmoid(z)=0.5 z趋于无穷大时,sigmoid(z)趋近于1,z趋于负无穷,值趋于0。为什么选择这样的激活函数呢?因为是模拟人脑的神经元。人脑的神经元也是把输入的信号做加权累加,然后看累加和是否超过一个“阈值”。如果超过,继续向下一个神经元发送信号,否则就不发送。因此人脑的神经元更像是一个阶跃函数:

007.png


   最早的感知机(Perception)其实用的就是这个激活函数。但是它有一个缺点就是0之外的所有点的导数都是0,在0点的导数是无穷大,所以很难用梯度的方法优化。而Sigmoid函数是处处可导。下面我手工推导了一下,如果大家不熟悉可以试着推导一下Sigmoid函数的导数,我们后面也会用到。

008.jpg


我们把许多的单个神经元按照层次组织起来就是多层的神经网络。

009.png


   比如我们的手写数字识别,输入层是784维,就是神经网络的地一层,然后中间有15个hidden(因为我们不知道它的值)神经元,然后输出层是10个神经元。中间隐层的每个神经元的输入都是784个原始像素通过上面的公式加权累加然后用sigmoid激活。而输出层的每一个神经元也是中间15个神经元的累加然后激活。上面的图就是一个3层的神经网络。

   输入一个28*28的图像,我们得到一个10维的输出,那么怎么分类呢?最直接的想法就是把认为最大的那个输出,比如输出是(10,11,12,13,14,15,16,17,18,19),那么我们认为输出是9。

   当然,更常见的做法是最后一次经过线性累加之后并不用Sigmoid函数激活,而是加一个softmax的函数,让10个输出加起来等于1,这样更像一个 概率。而我们上面的情况,虽然训练数据的输出加起来是1,但是实际给一个其它输入,输出加起来很可能不是1。不过为了与Nielsen的文章一致,我们还 是先用这种方法。

   因此,假设我们有了这些参数【总共是784*15 + 15(w0或者叫bias) + 15*10 + 10】,我们很容易通过上面的公式一个一个的计算出10维的输出。然后选择最大的那个作为我们识别的结果。问题的难点就在怎么 选择这么多参数,然后使得我们分类的错误最少。

   而我们怎么训练呢?对于一张图片,假设它是数字“1”,那么我们期望它的输出是(0,1,0,0,0,0,0,0,0,0),所以我们可以简单的用最小平方错误作为损失函数。不过你可能会有些疑问,我们关注的指标应该是分类的“正确率”(或者错误率),那么我们为什么不直接把分类的错误率作为损失函数呢?这样神经网络学习出来的参数就是最小化错误率。

   主要的原因就是错误率不是参数的连续函数。因为一个训练数据如果分类正确那么就是1,否则就是0,这样就不是一个连续的函数。比如最简单的两类线性分类器,f(x)=w0+w1*x1+w2*x2。如果f(x)>0我们分类成类别1;否则我们分类成类别2。如果当前的w0+w1*x1+w2*x2<0,我们很小的调整w0(或者w1,w2),w0+w1*x1+w2*x2仍然小于0,【事实上对于这个例子,只要是w0变小,他们的累加都是小于0的】所以f(x)的值不会变化,而w0一直增大到使累加和等于0之前都不会变化,只有大于0时突然变成1了,然后一直就是1。因此之前的错误率都是1,然后就突然是0。所以它不是个连续的函数。

   因为我们使用的优化算法一般是(随机)梯度下降的算法,在每次迭代的时候都是试图做一个微小的参数调整使得损失变小,但是不连续的函数显然也不可导,也就没法用这个算法来优化参数。

   因此我们使用了最小平方误差(MSE)损失函数。

010.png


   y(x)就是神经网络的输出,可能写成f(x)大家会习惯一点。a是目标的输出,比如当前分类是数字1,那么我们期望的输出就是(0,1,0,0,0,0,0,0,0,0)。

   首先这个损失函数是参数w的连续函数,因为y(x)就是神经网络的输出,每个神经元都是它的输入的线性加权累加,然后使用sigmoid激活函数【如果使用最早的阶跃函数就不连续了,所以后来使用了Sigmoid函数】,然后每一层的神经元都是用上一层的神经元通过这样的方式计算的(只不过每个神经元的参数也就是权重是不同的数值而已),所以这些连续函数的复合函数也是连续的。

   其次这个损失函数和我们的最终优化目标是“大致”一致的。比如C(w,b)趋于0时,它就要求y(x)趋于a,那么我们的分类也就趋于正确。当然可能存在一种极端的情况,比如有3个训练数据,第一组参数,它分类正确了2个训练数据,但是错的那1个错的很“离谱”,也就是y(x)和a差距极大;而第二组参数,他正确分类了1个训练数据,但是错的那两个都还不算太差。那么这种情况下MSE和正确率并不一致。

2.2.3 随机梯度下降(Stochastic Gradient Descent)和自动求梯度(Automatic Derivatives)

   上面说了,我们有了一个参数化的模型,训练的过程就是根据训练数据和loss function,选择“最优”的参数,使得loss“最小”,这从数学上来讲就是一个优化问题。这看起来似乎不是什么值得一提的问题,也许你还记得微积 分里的知识,极值点的各种充分必要条件,比如必要条件是导数是0,然后直接把参数解出来。但在现实生活中的函数远比教科书里学到的复杂,很多模型都无法用 解析的方式求出最优解。所以现实的方法就是求“数值”解,一般最常见的方法就是迭代的方法,根据现在的参数,我们很小幅度的调整参数,使得loss变小一 点点。然后一步一步的最终能够达到一个最优解(一般是局部最优解)。那怎么小幅调整呢?像闷头苍蝇那样随机乱试显然效率极低。因此我们要朝着一个能使函数 值变小的方向前进。而在一个点能使函数值变小的方向有无穷多个,但有一个方向是下降速度最快的,那就是梯度。因此更常见的方法就是在当前点求函数的梯度, 然后朝着梯度的方向下降。朝梯度的方向走多远呢?一般走一个比较小的值是比较安全的,这个值就是“步长”。一般刚开始随机的初始化参数,loss比较大, 所以多走一些也没关系,但是到了后面,就不能走太快,否则很容易错过最优的点。

   因为loss是所有训练数据的函数,所以求loss的梯度需要计算所有的训练数据,对于很多task来说,训练数据可能上百万,计算一次代价太大,所以一 般会“随机”的采样少部分数据,比如128个数据,求它的梯度。虽然128个点的梯度和一百万个的是不一样的,但是从概率来讲至少是一致的方向而不会是相 反的方向,所以也能使loss变小。当然这个128是可以调整的,它一般被叫做batch size,最极端的就是batch是1和一百万,那么分别就是online learning和退化到梯度下降。batch size越大,计算一次梯度的时间就越久【当然由于GPU和各种类似SSE的指令,一次计算128个可能并不比计算1个慢多少】,随机梯度和真正梯度一致 的概率就越大,走的方向就更“正确”;batch size越小,计算一次的时间就越短,但可能方向偏离最优的方向就更远,会在不是“冤枉路”。但实际的情况也很难说哪个值是最优的,一般的经验取值都是几 十到一两百的范围,另外因为计算机都是字节对齐,32,64,128这样的值也许能稍微加快矩阵运算的速度。但是实际也很多人选择10,50,100这样 的值。

   除了常见的随机梯度下降,还有不少改进的方法,如Momentum,Adagrad等等,有兴趣的可以看看 http://cs231n.github.io/neural-networks-3/#update ,里面还有个动画,比较了不同方法的收敛速度的比较。

通过上面的分析,我们把问题变成了怎么求loss对参数W的梯度。

求梯度有如下4种方法:

1.手工求解析解

比如 f(x)=x^2, df/dx=2*x。然后我们要求f(x)在x=1.5的值,代进去就2*1.5=3

2.数值解

使用极限的定义:

011.png


3.机器符号计算

让机器做符号运算,实现1的方法,但是机器如果优化的不好的话可能会有一些不必要的运算。

比如 x^2 + 2*x*y + y^2,直接对x求导数变成了 2*x + 2*y,两次乘法一次加分,但是我们可以合并一下变成2*(x+y),一次乘法一次加分。

4.自动梯度

下面我会在稍微细讲一下,所以这里暂时跳过。

这些方法的优缺点:
 

  • 手工求解“数学”要求高,有可能水平不够求不对,但效率应该是能最优的。

  • 没任何函数,甚至没有解析导数的情况下都能使用,缺点是计算量太大,而且只是近似解【因为极限的定义】,在某些特别不“连续”的地方可能误差较大。所以实际使用是很少,只是用它来验证其它方法是否正确。

  • 机器符号计算,前面说的,依赖于这个库的好坏。



实际的框架,如TensorFlow就是自动梯度,而Theano就是符号梯度。

2.2.4 编程实战

   通过上面的介绍,我们其实就可以实现一个经典的前馈(feed forward)神经网络了,这种网络结构很简单,每一层的输入是前一层的输出。输入层没有输入,它就是原始的信号输入。而且上一层的所有神经元都会连接到下一层的所有神经元,就像我们刚才的例子,输入是784,中间层是15,那么就有785*15个连接【再加上每个中间节点有一个bias】。所以这种网络有时候也加做全连接的网络(full connected),用来和CNN这种不是全连接的网络有所区别,另外就是信号是从前往后传递,没有反馈,所以也叫前溃神经网络,这是为了和RNN这种有反馈的区别。

   当然,我们还没有讲怎么计算梯度,也就是损失函数相对于每一个参数的偏导数。在下一部分我们会详细讨论介绍,这里我们先把它当成一个黑盒的函数就好了。

1.代码
我们这里学习一下Nielsen提供的代码。代码非常简洁,只有不到100行代码。
https://github.com/mnielsen/neural-networks-and-deep-learning

git clone https://github.com/mnielsen/neural-networks-and-deep-learning.git

2.运行

创建一个 test_network1.py,输入如下代码:
import mnist_loader
import network

training_data, validation_data, test_data = mnist_loader.load_data_wrapper()
net = network.Network([784, 30, 10])
net.SGD(training_data, 30, 10, 3.0, test_data=test_data)
保存后直接运行 Python test_network1.py。这里我们让他进行了30次迭代,最终在测试数据上的准确率大概在95%左右(当然因为随机初始化参数不同,最终的结果可能有所不同)
Epoch 0: 8250 / 10000
Epoch 1: 8371 / 10000
Epoch 2: 9300 / 10000
......
Epoch 28: 9552 / 10000
Epoch 29: 9555 / 10000
3. 代码阅读

   Python代码很容易阅读,即使之前没有用过,稍微学习两天也就可以上手,而且大部分机器学习相关的代码不会用到太复杂的语言特性,基本就是一些数学的线性代数的运算。而Python的numpy这个库是用的最多的,后面阅读代码的时候我会把用到的函数做一些介绍,继续下面的阅读之前建议花十分钟阅读一下 http://cs231n.github.io/python-numpy-tutorial/。

3.1 mnist_loader.load_data_wrapper函数

   这个函数用来读取mnist数据,数据是放在data/mnist.pkl.gz。首先这是个gzip的压缩文件,是Pickle工具序列化到磁盘的格式。不熟悉也没有关系,反正我们知道这个函数的返回值就行了。

   这个函数返回三个对象,分别代表training_data,validation_data和test_data。

   training_data是一个50,000的list,然后其中的每一个元素是一个tuple。tuple的第一个元素是一个784维的numpy一维数组。第二个元素是10维的数组,也就是one-hot的表示方法——如果正确的答案是数字0,那么这个10维数组就是(1, 0, 0, …)。

   而validation_data是一个10,000的list,每个元素也是一个tuple。tuple的第一个元素也是784维的numpy一维数组。第二个元素是一个0-9的数字,代表正确答案是那个数字。

   test_data的格式和validation_data一样。

   为什么training_data要是这样的格式呢?因为这样的格式计算loss更方便一些。

3.2 Network类的构造函数

   我们在调用net = network.Network([784, 30, 10])时就到了init函数。为了减少篇幅,代码里的注释我都去掉了,重要的地方我会根据自己的理解说明,但是有空还是值得阅读代码里的注释。
class Network(object):
def __init__(self, sizes):self.num_layers = len(sizes)
self.sizes = sizes
self.biases = [np.random.randn(y, 1) for y in sizes[1:]]
self.weights = [np.random.randn(y, x)
for x, y in zip(sizes[:-1], sizes[1:])]
   比如上面的参数,我们保存下来的self.num_layers=3,也就是3层的网络。每一层的神经元的个数保存到self.sizes里。接下来就是构造biases数组并随机初始化。因为输入层是没有参数的,所以是for y in sizes[1:],我们使用了numpy的random.randn生成正态分布的随机数用来作为参数的初始值。注意这里生成了2维的随机变量。回忆一下,如果我们有30个hidden unit,那么bias的个数也是30,那就生成一个30维的1维数组就行了,为什么要是30*1的二维数组呢?其实用1维也可以,不过为了和weights一致,后面代码方便,就用二维数组了。另外weights也是一样的初始化方法,不过注意randn(y,x)而不是randn(x,y)。比如对于我们输入的[784,30,10],weights分别是30*784和10*30的。当然其实weights矩阵转置一下也可以,就是计算矩阵乘法的时候也需要有一个转置。不同的文献可能有不同的记法,但是我们在实现代码的时候只需要随时注意矩阵的大小,检查矩阵乘法满足乘法的约束就行了,矩阵AB能相乘,必须满足的条件是B的列数等于A的函数就行。

   对于Nielsen的记法,矩阵的每一行就是一个神经元的784个参数,那么weights(30*784) * input(784*1)就得到30个hidden unit的加权累加。

3.3 feedforward函数

   给点输入a(784维),计算最终神经网络的输出(10维)。
def feedforward(self, a):
"""Return the output of the network if ``a`` is input."""for b, w in zip(self.biases, self.weights):
a = sigmoid(np.dot(w, a)+b)
return a
   代码非常简单,这里用到了np.dot,也就是矩阵向量的乘法,此外这里有一个Sigmoid函数,这个函数的输入是numpy的ndarray,输出也是同样大小的数组,不过对于每个元素都进行了sigmoid的计算。用numpy的术语就是universal function,很多文献里一般都叫elementwise的function。我觉得后面这个名字更直接。
#### Miscellaneous functionsdef sigmoid(z):
"""The sigmoid function."""return 1.0/(1.0+np.exp(-z))

def sigmoid_prime(z):
"""Derivative of the sigmoid function."""return sigmoid(z)*(1-sigmoid(z))
上面就是Sigmoid函数,另外也把sigmoid_prime,也就是Sigmoid的导数放在了一起【不记得的话看前面Sigmoid的导数的推导】。

3.4 SGD函数

这个函数是训练的入口,比如我们之前的训练代码:
net.SGD(training_data, 30, 10, 3.0, test_data=test_data)


def SGD(self, training_data, epochs, mini_batch_size, eta,
test_data=None):
if test_data: n_test = len(test_data)
n = len(training_data)
for j in xrange(epochs):
random.shuffle(training_data)
mini_batches = [
training_data[k:k+mini_batch_size]
for k in xrange(0, n, mini_batch_size)]
for mini_batch in mini_batches:
self.update_mini_batch(mini_batch, eta)
if test_data:
print "Epoch {0}: {1} / {2}".format(
j, self.evaluate(test_data), n_test)
else:
print "Epoch {0} complete".format(j)
第一个参数就是training_data。

第二个参数就是epochs,也就是总共对训练数据迭代多少次,我们这里是30次迭代。

第三个参数是batch大小,我们这里是10,最后一个参数是eta,也就是步长,这里是3.0。除了网络结构(比如总共多少个hidden layer,每个hidder layer多少个hidden unit),另外一个非常重要的参数就是步长。前面我们也讨论过了,步长太小,收敛速度过慢,步长太大,可能不收敛。实际的情况是没有一个万能的准则,更多的是根据数据,不停的尝试合适的步长。如果发现收敛太慢,就适当调大,反之则调小。所以要训练好一个神经网络,还是有很多tricky的技巧,包括参数怎么初始化,激活函数怎么选择,比SGD更好的优化算法等等。

第四个参数test_data是可选的,如果有(我们的例子是穿了进来的),则每次epoch之后都测试一下。

代码的大致解释我用注释的形式嵌在代码里了:
  for j in xrange(epochs): ## 一共进行 epochs=30 轮迭代
random.shuffle(training_data) ## 训练数据随机打散
mini_batches = [
training_data[k:k+mini_batch_size]
for k in xrange(0, n, mini_batch_size)] ## 把50,000个训练数据分成5,000个batch,每个batch包含10个训练数据。
for mini_batch in mini_batches: ## 对于每个batch
self.update_mini_batch(mini_batch, eta) ## 使用梯度下降更新参数
if test_data: ## 如果提供了测试数据
print "Epoch {0}: {1} / {2}".format(
j, self.evaluate(test_data), n_test) ## 评价在测试数据上的准确率
else:
print "Epoch {0} complete".format(j)
下面是evaluate函数:
def evaluate(self, test_data):
test_results = [(np.argmax(self.feedforward(x)), y)
for (x, y) in test_data]
return sum(int(x == y) for (x, y) in test_results)
   对于test_data里的每一组(x,y),y是0-9之间的正确答案。而self.feedforward(x)返回的是10维的数组,我们选择得分最高的那个值作为模型的预测结果np.argmax就是返回最大值的下标。比如x=[0.3, 0.6, 0.1, 0, ….],那么argmax(x) = 1。

   因此test_results这个列表的每一个元素是一个tuple,tuple的第一个是模型预测的数字,而第二个是正确答案。

所以最后一行返回的是模型预测正确的个数。
 
3.5 update_mini_batch函数
def update_mini_batch(self, mini_batch, eta):
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
for x, y in mini_batch:
delta_nabla_b, delta_nabla_w = self.backprop(x, y)
nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
self.weights = [w-(eta/len(mini_batch))*nw
for w, nw in zip(self.weights, nabla_w)]
self.biases = [b-(eta/len(mini_batch))*nb
for b, nb in zip(self.biases, nabla_b)]
它的输入参数是mini_batch【size=10的tuple(x,y)】和eta【3.0】。
def update_mini_batch(self, mini_batch, eta):
nabla_b = [np.zeros(b.shape) for b in self.biases]
## 回忆一下__init__,biases是一个列表,包含两个矩阵,分别是30*1和10*1
## 我们先构造一个和self.biases一样大小的列表,用来存放累加的梯度(偏导数)
nabla_w = [np.zeros(w.shape) for w in self.weights]
## 同上, weights包含两个矩阵,大小分别是30*784和10*30
for x, y in mini_batch:
delta_nabla_b, delta_nabla_w = self.backprop(x, y)
## 对于一个训练数据(x,y)计算loss相对于所有参数的偏导数
## 因此delta_nabla_b和self.biases, nabla_b是一样大小(shape)
## 同样delta_nabla_w和self.weights,nabla_w一样大小
nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
## 把bias的梯度累加到nabla_b里
nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
## 把weight的梯度累加到nable_w里
self.weights = [w-(eta/len(mini_batch))*nw
for w, nw in zip(self.weights, nabla_w)]
## 使用这个batch的梯度和eta(步长)更新参数weights
self.biases = [b-(eta/len(mini_batch))*nb
for b, nb in zip(self.biases, nabla_b)]
## 更新biases
## 这里更新参数是除了batch的大小(10),有的人实现时不除,其实没有什么区别,因为超参数eta会有所不同,如果不除,那么eta相当于是0.3(在eta那里就除了batch的大小了)。
3.6 backprop函数

这个函数就是求loss相对于所有参数的偏导数,这里先不仔细讲解,等下次我们学习梯度的求解方法我们再回来讨论,这里可以先了解一下这个函数的输入和输出,把它当成一个黑盒就行,其实它的代码也很少,但是如果不知道梯度的公式,也很难明白。
def backprop(self, x, y):
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
# feedforwardactivation = x
activations = [x] # list to store all the activations, layer by layerzs = [] # list to store all the z vectors, layer by layerfor b, w in zip(self.biases, self.weights):
z = np.dot(w, activation)+b
zs.append(z)
activation = sigmoid(z)
activations.append(activation)
# backward passdelta = self.cost_derivative(activations[-1], y) * \
sigmoid_prime(zs[-1])
nabla_b[-1] = delta
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
for l in xrange(2, self.num_layers):
z = zs[-l]
sp = sigmoid_prime(z)
delta = np.dot(self.weights[-l+1].transpose(), delta) * sp
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
return (nabla_b, nabla_w)
   它的输入就是一个训练样本(x,y)分别是784*1和10*1。输出就是和self.biases,self.weights一样大小的列表,然后列表中的每一个数组的大小也是一样。具体到上面的例子,输出nabla_b包含两个矩阵,大小分别是30*1和10*1;nabla_w也包含两个矩阵,大小分别是30*784和10*30。
 
未完待续

作者简介:李理,目前就职于环信,即时通讯云平台和全媒体智能客服平台,在环信从事智能客服和智能机器人相关工作,致力于用深度学习来提高智能机器人的性能。
责编:周建丁(zhoujd@csdn.net)
收起阅读 »

环信感恩一路有你

感恩是一种陪伴;感恩是一种亲情;感恩是一起栉风沐雨;感恩是一起砥砺前行;感恩一路有你!
感恩是一种陪伴;感恩是一种亲情;感恩是一起栉风沐雨;感恩是一起砥砺前行;感恩一路有你!


262563490636374582.jpg

花式营销渐远,懂用户的服务才有未来

    解缙有语“山间竹笋,嘴尖皮厚腹中空”,如果说营销是靠传播、销售的“尖嘴”来吸引用户,并厚着脸皮撮合交易,那么服务或产品承载的服务则是腹中之物,唯有货真价实才能让用户对你有真实的感受,才能带来粘性及口碑。 花式杂耍将被新消费技术背书的服...
继续阅读 »
    解缙有语“山间竹笋,嘴尖皮厚腹中空”,如果说营销是靠传播、销售的“尖嘴”来吸引用户,并厚着脸皮撮合交易,那么服务或产品承载的服务则是腹中之物,唯有货真价实才能让用户对你有真实的感受,才能带来粘性及口碑。

7348.jpg_wh860_.jpg



花式杂耍将被新消费技术背书的服务不断打脸

   今天,服务在很多人眼里,还停留在售后层面;即使是被移动互联网潮水不断 “打脸”,虽然BAT在不断强调这是一个被服务所承载的用户体验时代,但传统行业及很多企业依然将服务定义为售后,似乎成了墨守成规,而且很多企业把服务中心看成为成本中心,谈起对企业的贡献就要划清界限。

   在这些企业眼里,大部分商品交易是一锤子买卖,售前你是上帝,售后你是魔鬼。过度追求利益和强调回报率,导致出现“有钱就服务,没钱就示忙”的“坏圈”,即使宣称有售后,其实是售了就没有以后。

   前段时间微博流传一个案例,和我们平时拨打银行的客服一样,某低额储户致电某大银行客服,打几分钟了还提示繁 忙,最后进入语音留言;但这个用户很聪明,在留言中表示自己要咨询存款理财的事情,银行的客服电话马上就回拨过来了,接着这个用户对客服痛斥一番,银行服务人员最后表示了道歉。这些年,许多涉足金融业的互联网公司用哪怕是一点传统银行不肯让惠于用户的小甜头服务,就已让传统银行处于“落花流水春去也”之态势;如果它们还固守“‘有钱’就给服务,‘没钱’无限等待”的思维,在未来金融业全面开放后的竞争中一定会被完全颠覆。

   就算是重营销,但很多大企业的营销模式依然是老套路。笔者最近接到不少楼盘、理财类销售电话,外呼似乎依然是这些企业的主要营销手段,而笔者所在的通信运营商,主动的营销手段也以短信通知及外呼营销为主,而显然短信通知已渐渐成为用户遗忘的糟粕,外呼营销虽然给用户带来骚扰感,但当前依然用,只是利用规模效应来拉量。两者由于无法在用户最合适的时间进行营销买卖,所以容易被用户所排斥,不懂用户的营销就是骚扰,相关整治已经上升到了国家层面。

   在前沿技术上的革新上,担任“万众创新”主力部队的国内民营企业,更多的是在营销模式上机关算尽,从饥饿营销到病毒传播,玩得不亦乐乎;而在产品创新及黑科技深度上,永远都是走着模仿的套路,走出去就吃专利官司,唯有在国内躺着享受新营销模式的红利,于是有了某宝的“店大欺客”,“屌丝米”舶来乔布斯营销模式成就400亿估值,但无法持续满足用户对产品服务体验的追求,便真验证了成也萧何败也萧何,目前估值下降到40亿。

   服务,是一种脚踏实地的营销模式,而且是一种基于用户需求和互动的长营销。大多数服务隐藏着高价值营销需求,特别是在泛金融、电商、通信等行业。以通信运营商为例,终端、手机套餐或宽带产品被用户购买后,意味着销售工作才只是开始而已,不应该只关注最开始的卖出环节,客服中心是服务营销的主体机构,不应该被简单看成成本中心。市场营销部门知道哪款产品卖得好,但不一定知道为何卖得好、用户最喜欢的部分是什么,不能第一时间获取用户群的声音。服务是产品设计、优化产品和迭代的重要“情报站”。未来,重营销藐服务者,必定会被市场抛弃。

下面笔者分享一下最近发生的两个案例:
 
万达老王驳斥营销的规划,卖完不管后边是不懂商业规律。

   前不久网上发布了一段王健林日常工作的视频,王健林对汇报的下属说“不能看营销人修改的东西,他们是卖完不管后边,着重的是后边接手的人员,没有全局(观),是很要命的”。

   同样,下属汇报合肥项目商业地产开发项目说考虑到将来做餐饮、冷饮,在走廊里面设计一些休息室,还摆一些小座椅什么的。王驳斥:不能开玩笑,这么一弄彻底死了,送菜送汤把走廊全部弄得脏兮兮的。

   另外有一个项目,主管的设计方名称叫做:黄金屋项目,王训斥:黄金屋多么的土啊,太蠢了,完全土豪。像叫红灯笼、金编钟这样体验中国文化的名称不是挺好的吗?

懂得用户,是懂得商业规律的一种体现。做营销的不会管客户后续反应如何,只管卖出去,是否退货,客户是否抱怨,口碑是否差,不管了。

   笔者并不是否定营销,我们应该反过来看,做市场营销都是为了服务做支撑的,而不是服务为市场开拓者擦屁股。服务,不管是售后还是售前。营销主要是撮合交易,但用户使用后的感知是基于对服务的体验,“做实”服务、懂用户,才是真懂商业规律。
 
360老周当客服,重视用户声音的产品经理才是好BOSS。

   10月初,360的老周在微薄公开说要当客服了,其实一直以来,老周在微博、微信上与用户互动得很好,经常见到其亲自上阵解决问题。

   360官方微信号文章中描述:要解决这个问题,就要把“用户至上,体验为王”真正贯彻为360团队的文化,这也是互联网思维最基本的点。客服作为一线的、最直接和客户接触的群体,他们有很直观和很深刻理解。所以做好客服,接收到来自各方的反馈,公司才能不断完善产品,让产品更接近用户的需求点。

   很多产品经理说,我的产品很好啊,为何用户不买账。因为产品经理只看到产品的亮点,而用户可能被亮点吸引,然而很多产品所谓的亮点都不够说服用户来体验,或许会因为冲动消费了一把,但没有服务的闭环来支撑体验感知,没有形成粘性和口碑,品牌得不到持续的发展。今天,连互联网的大佬们都要亲自上门做服务,正如老周所说: “自己的狗粮自己都不吃怎么行”。

   服务是用户声音通道,不重视用户的声音,用户会买账吗?会被持续忽悠?

   很多市场营销者,对服务嗤之以鼻,认为自己在前线开拓“江山”,是利润中心;但是客户靠什么来维系呢?市场开拓者说,用钱砸用户关系就行了,但是钱能一直烧下去吗?美团、大众点评烧得不少吧,滴滴、Uber烧得不少吧。诚然,市场营销团队是战场上的主力;但服务团队不仅承担了粮草物资筹备运输,还有用户的情报收集角色。
 
未来经营用户的服务凸显,运营产品思维的营销渐消

   而随着“智能+”时代的到来,基于用户消费需求的数据对比中心逐渐取代营销将成为趋势,这个信用中心将所有商品及服务从质量、口碑等维度进行对比,并指导用户进行理性选择,当然非理性选择依然存在,但无论花式营销多么成功都无法掩盖产品或者服务本身的问题。

   今天,花式营销可以把石头包装成为钻石,未来石头就是石头,花式营销将无法在智能技术及信用数据面前遁形;而服务将从懂用户中凸显其价值,成为企业长青的最好背书!
 
本文原载于《客户世界》2016年11月刊;作者曾岳衡,广东移动(江门)客服中心。 收起阅读 »

iOS客户端集成环信3.0详解

说明: 集成的SDK版本 V3.2.1  版本时间2016-11-12    集成时间2016-11-21 前提: 观看视频,视频地址如下: 环信SDK集成:http://www.imgeek.org/video/40 环信EaseUI集成:...
继续阅读 »
说明:
集成的SDK版本 V3.2.1 
版本时间2016-11-12   
集成时间2016-11-21

前提:
观看视频,视频地址如下:
环信SDK集成:http://www.imgeek.org/video/40
环信EaseUI集成:http://www.imgeek.org/video/39 

具体集成过程:
 一: 添加依赖库


 1. CoreMedia.framework
 2. AudioToolbox.framework
 3. AVFoundation.framework
 4. 4.MobileCoreServices.framework
 5. ImageIO.framework
 6. libc++.tbd
 7. libz,tbd
 8. libsqlite3.tbd
 9. libstdc++.6.0.9.tbd
 10. Foundation.framework
 11. libiconv.tbd    (Xcode7以上)


最终效果如图:

2910225-5b4ea705f8927b27.jpg



二:一些设置 

1.修改info.plist文件,适配iOS9以上系统,在info.plist中添加
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

2.修改info.plist文件,适配iOS10.0,在info.plist中添加

为了调用相册,地理位置,话筒,照相机,通讯录
<key>NSPhotoLibraryUsageDescription</key>
<string>中华人需要使用相册服务获取照片</string>
<key>NSCameraUsageDescription</key>
<string>中华人需要使用照相机服务进行拍照获取照片</string>
<key>NSContactsUsageDescription</key>
<string>中华人需要使用通讯录服务进行好友邀请</string>
<key>NSMicrophoneUsageDescription</key>
<string>中华人需要使用话筒进行吐槽反馈信息</string>
<key>NSLocationUsageDescription</key>
<string>中华人需要使用定位服务来发送位置信息</string>

3.Bulid Setting 设置bitcode 为NO,如图:


2910225-be665cae8d135c76.jpg



4.新建pch文件,如图:


2910225-54390668176db8cd.jpg



进行设置,如图:


2910225-82defb53b6ca36f0.jpg



三:Appdelegate
#import "AppDelegate.h"
#import "ChatViewController.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.

// SDK的初始化
/**
* EMOptions类是SDK的配置信息
* 可以加入推送证书的名称
*/
EMOptions *options = [EMOptions optionsWithAppkey:@"luoxiaoyong#mingxin"];
options.apnsCertName = @"Push_dev";

// 初始化SDK
EMError *error = [[EMClient sharedClient] initializeSDKWithOptions:options];
if (!error) {
NSLog(@"初始化成功");
}

// 登录
error = [[EMClient sharedClient] loginWithUsername:@"xrdaly001" password:@"123456"];

if (!error) {

NSLog(@"登录成功");
}
else {

NSLog(@"登录失败");
}

self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
ChatViewController *vc = [[ChatViewController alloc] initWithConversationChatter:@"xrdaly002" conversationType:EMConversationTypeChat];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
self.window.rootViewController = nav;

[self.window makeKeyAndVisible];

return YES;
}
四:添加键盘表情(说明)

在EaseMessageViewController.m的viewDidLoad方法结尾处加上
EaseEmotionManager * manager = [[EaseEmotionManager alloc] initWithType:EMEmotionDefault emotionRow:3 emotionCol:7 emotions:[EaseEmoji allEmoji]];
[self.faceView setEmotionManagers:@[manager]];

五:国际化文件乱码问题
下载demo,直接把demo中的国际化文件拷贝到现在项目中,ok


以上就是实现环信单聊的全部过程,,如有任何疑问欢迎加Q:714700382,我们共同讨论,共同进步 收起阅读 »

后移动互联网时代App创新的挑战 【北京站】

2016已接近尾声,微信小程序的推出给互联网行业掀起了一阵风浪,网上流传小程序的内测账号甚至被炒到了300万!面对小程序的强势袭来,App的时代真的要过去了吗? 本次活动APICloud联合图普和智齿跟大家聊聊,后互联网时代,App如何创新?  ...
继续阅读 »
2016已接近尾声,微信小程序的推出给互联网行业掀起了一阵风浪,网上流传小程序的内测账号甚至被炒到了300万!面对小程序的强势袭来,App的时代真的要过去了吗?

本次活动APICloud联合图普和智齿跟大家聊聊,后互联网时代,App如何创新? 

活动议程.png


13:30-14:00   签到

14:00-15:00   后移动互联网时代App创新的挑战

微信小程序的推出是否会改变现有的移动格局?

后移动互联网时代的App应如何创新?

15:00-15:40   如何利用人工智能使你App差异化

15:40-16:20   后移动互联网时代的客户服务变革与创新

16:20-16:25   抽奖

16:25       活动结束&自由交流

分享嘉宾.png


APICloud创始人兼CEO 刘鑫

刘鑫,APICloud移动应用云服务创始人兼CEO,见证了中国移动互联网从SP梦网到智能机时代的全过程。专注于国内外移动应用开发平台领域的研究,系统的阐述过Web App的挑战与Hybrid App的发展。并且凭借APICloud.com移动应用云服务成功拿到北极光领投的A轮融资。

刘鑫.jpg


图普科技市场负责人 李麟

曾在新浪,电信负责市场及战略合作,经历过两次创业,在产品,运营,市场等方面有丰富的实战经验。

李麟.jpeg


智齿科技产品总监 杨兴成

智齿科技产品总监,曾任凤凰网产品经理,负责凤凰新闻媒体开放平台、凤凰网APP;智齿客服,移动时代的智能客服,专注为企业提供人工智能服务。

杨兴成.png




活动概况.png


活动时间:2016年11月26日(周六)13:30-16:30
活动地点:义创空间—(北京)小样青年社区(北京市海淀区中关村创业大街甲一号鼎好大厦A座扶梯口上2楼)


map.jpeg



合作伙伴.jpeg


 
报名链接:http://www.huodongxing.com/event/5361525571400
  收起阅读 »

Erlang之父Joe Armstrong今日到访环信,激情讲述如何构建一个永不停机的系统

   据环信社报道:今日,61岁的Erlang之父Joe Armstrong不远万里来到中国,与环信程序猿一行举行了亲切的会谈。Joe分享了对如何学习编程,如何成为大牛等一系列热门问题提出了自己的看法。    大神Joe激情讲述如何用Erlang特...
继续阅读 »

004.jpg


   据环信社报道:今日,61岁的Erlang之父Joe Armstrong不远万里来到中国,与环信程序猿一行举行了亲切的会谈。Joe分享了对如何学习编程,如何成为大牛等一系列热门问题提出了自己的看法。


001.jpg


   大神Joe激情讲述如何用Erlang特性来构建一个永远不停机的系统。Joe表示:“一个永不停机的系统需要能够在多种失败的情况下依然能够保持正常运行。特别在系统维护和升级方面,Erlang独有的热代码替换有着天然的优势。”


002.jpg


   环信首席架构师&即时通讯云产品线负责人Eric向Joe介绍了Erlang在环信的部署情况:“Erlang在环信已经部署上千台机器,一个典型集群会使用高达3176核8068G内存,用来支持包括移动端、Web端、Windows和Linux等多个平台的千万级长连服务。”

  双方在友好、建设性的气氛中深入探讨就共同关心的重大问题深入交换意见,达成重要共识。


4]OD_DPUAMC8JN)BRO0[OT7.png


Eric向大神Joe赠送环信纪念品
   环信首席颜值担当Eric首先欢迎Joe Armstrong访问环信。他说,这是Erlang之父近2年来的首次访问环信,也是中国和瑞典程序猿交流中的一件大事。环信高度重视Joe的访问,相信此访必将对中瑞计算机语言长远发展产生新的推动。(新闻联播口音)

003.jpg


环信小粉丝获得大神Joe亲笔签名赠书

VEC}3ZXHMHQAKZ24CGBZ9G.png


为了帮助大家学习Erlang语言,大神Joe推荐给开发者的教材
Erlang特性:
  •  并发性 - Erlang支持超大量级的并发进程,并且不需要操作系统具有并发机制。
  •  分布式 - 一个分布式Erlang系统是多个Erlang节点组成的网络(通常每个处理器被作为一个节点)
  •  健壮性 - Erlang具有多种基本的错误检测能力,它们能够用于构建容错系统。
  •  软实时性- Erlang支持可编程的“软”实时系统,使用了递增式垃圾收集技术。
  •  热代码升级-Erlang允许程序代码在运行系统中被修改。旧代码能被逐步淘汰而后被新代码替换。在此过渡期间,新  旧代码是共存的。
  •  递增式代码装载-用户能够控制代码如何被装载的细节。
  •  外部接口-Erlang进程与外部世界之间的通讯使用和在Erlang进程之间相同的消息传送机制。
  •  Fail-fast(中文译为速错),即尽可能快的暴露程序中的错误。
  •  面向并发的编程(COP concurrency-oriented programming)
  •  函数式编程
  •  动态类型
  •  及早求值或严格求值
  •  脚本语言

关于Erlang和Joe Armstrong
Erlang得名于丹麦数学家及统计学家Agner Krarup Erlang,同时Erlang还可以表示Ericsson Language。
Erlang并非一门新语言,它出现于1987年,只是当时对并发、分布式需求还没有今天这么普遍,当时可谓英雄无用武之地。Erlang语言创始人Joe Armstrong当年在爱立信做电话网络方面的开发,他使用Smalltalk,可惜那个时候Smalltalk太慢,不能满足电话网络的高性能要求。但Joe实在喜欢Smalltalk,于是定购了一台Tektronix Smalltalk机器。但机器要两个月时间才到,Joe在等待中百无聊赖,就开始使用Prolog,结果等Tektronix到来的时候,他已经对Prolog更感兴趣,Joe当然不满足于精通Prolog,经过一段时间的试验,Joe给Prolog加上了并发处理和错误恢复,于是Erlang就诞生了。这也是为什么Erlang的语法和Prolog有不少相似之处,比如它们的List表达都是[Head | Tail]。
1987年Erlang测试版推出,并在用户实际应用中不断完善,于1991年向用户推出第一个版本,带有了编译器和图形接口等更多功能。1992年,Erlang迎来更多用户,如RACE项目等。同期Erlang被移植到VxWorks、PC和 Macintosh等多种平台,两个使用Erlang的产品项目也开始启动。1993爱立信公司内部独立的组织开始维护和支持Erlang实现和Erlang工具。
环信Erlang工程师招聘​
职位:通讯研发工程师(Erlang/Golang) 薪资范围: 25000以上
工作职责:
1. 负责开发和维护即时通讯系统,优化提高后端服务的承载能力;
2. 或负责优化、改进和实现IM协议,为移动互联网以及物联网用户提供可靠实时的即时通讯服务;
任职要求:
1. 熟悉Erlang、Golang或C/C++开发,有Unix/Linux平台相关经验者优先;
2. 熟悉网络通信机制及常用数据传输协议;
3. 熟悉常用数据库,如MySQL、Redis系统,有HBase或其他NoSQL相关使用经验者优先;
4. 有较强的解决问题能力,能够承受压力情况下完成解决问题;
5. 能够与团队成员紧密合作,共同攻克技术难题;
6. 掌握多种语言者优先,Go、Python或其他语言均可;
7. 需要有开放共享的心态,接受开源思想,有Github创建、维护或参与经验更好;
简历请发送至"talent@easemob.com"
收起阅读 »

环信移动客服v5.5.1更新:新增多机器人功能,完美适应不同场景

管理员模式 智能机器人界面优化    优化机器人设置页面,将欢迎语等设置合并至“自动回复”页签,将知识库合并至机器人设置。优化后,机器人相关的设置更集中,更方便使用。  欢迎语等合并至自动回复页签    机器人的欢迎语、默认回复...
继续阅读 »


0920.jpg_wh860_.jpg


管理员模式

智能机器人界面优化

   优化机器人设置页面,将欢迎语等设置合并至“自动回复”页签,将知识库合并至机器人设置。优化后,机器人相关的设置更集中,更方便使用。 

robot-0001.png


欢迎语等合并至自动回复页签

   机器人的欢迎语、默认回复、重复回复、超时回复、转人工设置合并至“自动回复”页签。 

robot-0002.png


知识库合并至机器人设置

   原来知识库页面的“知识规则”和“菜单素材库”页签合并至“机器人设置”页面,分别对应“知识规则”和“自定义菜单”。 

robot-0003.png


[机器人知识规则]

多机器人功能(增值服务)

   新增多机器人功能,支持创建机器人,并且为每个机器人单独设置基础信息、自动回复、知识规则、自定义菜单、场景智能应答。您还可以为新增的机器人绑定不同的关联,回答来自不同关联的用户的提问。

   多机器人功能为增值服务,如需开通,请联系环信商务经理。 
 
环信移动客服更新日志http://docs.easemob.com/cs/releasenote/5.5.1
 
环信移动客服登陆地址http://kefu.easemob.com/ 收起阅读 »

客服如何变为盈利中心?环信要把精准营销融入云客服

   环信起家于IM(即时通讯云),2014年6月产品上线,随着业务逐渐完善,公司在IM产品线基础上增加了更加垂直的云客服产品,在2014年10月全力押注云客服。为了巩固优势,又增加人工智能业务,即客服机器人。   经过一系列战略调整,环信的业务线包括即时通...
继续阅读 »


TRUNWW`KET)X`5KGM3596.png


   环信起家于IM(即时通讯云),2014年6月产品上线,随着业务逐渐完善,公司在IM产品线基础上增加了更加垂直的云客服产品,在2014年10月全力押注云客服。为了巩固优势,又增加人工智能业务,即客服机器人。
  经过一系列战略调整,环信的业务线包括即时通讯云、云客服和客服机器人三大块。

如何将这三者结合起来?

   环信的做法很直接,首先在手机上提供APP内置客服,这既符合客户服务移动化的时代大趋势,也是基于环信的看家本领-IM;其次将APP内置客服、微信客服、网页在线客服,电话呼叫中心客服等集成到统一的客服平台,提供全渠道客服;第三,加入机器人客服,节省人力,提高效率;第四,通过大数据和人工智能完善客户画像,帮助企业精准营销。

   通过这套业务逻辑,环信将打通客服与精准营销,让客服成为一个节约成本、创造利润的部门。

经过市场洗牌,IM业务站稳脚跟

   虽然IM是个老业务,在1998年就已经面世,但是直到微信的出现才被推到了顶峰,引发了市场对社交的追捧。许多互联网企业纷纷在产品中嵌入IM功能,发掘产品的社交属性。这也导致了移动IM云厂商应运而生。

   环信就是其中之一,IM是环信最早的产品,通过将IM能力打包成云服务和SDK,接入到客户的APP,使之拥有微信、陌陌这类社交软件的IM功能,为客户的APP添加社交属性。
 
   首先是2014年移动IM云市场出现,到2015年,最多时有14家IM服务厂商在争夺市场,其中包括阿里的阿里悟空、阿里百川和腾讯云通讯IM等,市场竞争激烈。但是随着洗牌和市场格局基本确定,大部分厂商都已离场,只剩下几家公司留存,环信成为其中主要的参与者,无论是在IM深耕还是向其他领域延伸,环信都有很大空间。
 
   在业务模式上,环信的IM分公有云和私有云二块业务。公有云主要面向互联网企业,采用按日活方式收费。IM私有云业务主要面向中大型私企,国企,政府,受监管的行业如银行证券等。经过阿里钉钉和企业微信的教育,国内企业都IM是移动办公系统的核心基础。但钉钉和企业微信目前只被中小企业接受,大型企业普遍倾向于IM私有云方案。目前环信的即时通讯云一共聚集了8万APP,未来仍将是重点发展的方向,是公司的现金牛和并未其他业务导流。

由IM切入客服,帮助客户实现精准营销

   云客服是IM应用场景的延伸。IM天生就有2种场景。一种是即时通讯云实现的连接人与人的社交功能,一种是云客服实现的人和商家之间沟通,实质上是连接人与商业。不管是淘宝旺旺,还是微信公共账号,本质都是用IM的方式让人(消费者)和商家更好的沟通和服务。

具体来看,环信的云客服分为六个部分。


全媒体接入,通过APP端的IM、网页在线聊天、微信、微博、邮件、工单、电话呼叫中心,七种形态接入统一的客服系统;

客服平台,作为工具性平台提供基础客服功能;

客户旅程透析,分析跨媒体跨渠道客服接入环境下的客户旅程及客户体验;

用户忠诚度运营,提供360度客户画像;

客服机器人,降低人力成本;

精准营销,根据在客户旅程透析、用户忠诚运营360度画像和客服机器人积累起来的数据,为商家提供精准营销。


   这六部分综合起来覆盖企业的服务到营销的完整闭环,是环信云客服竞争力所在。

   目前在云客服这块已经积累2.9万用户(包括最早免费用户)。其中教育、电商和金融是占比最大的行业,国美在线、楚楚街、泰康在线、新东方、中信证券等都是环信的客户。

对标Zendesk,不仅是Zendesk

   与市值超过22亿美元的云客服上市公司Zendesk对比,环信的业务线更长。除了IM、客服平台、客户行为分析、客服机器人这类相互重合的业务,环信还从客服延伸到营销销售。不像Zendesk仅作为客服工具,环信在提供客服工具之外更是帮助客户销售产品。

   Zendesk目前付费客户达到81,000,客单价较低,约3,000美元左右,由于大部分客户是中小客户,客服需求少,付费也就少,自然拉低了客单价。而环信这边的两种收费标准:

   1500元/坐席/年、4800元/坐席/年,与Zendesk收费标准相似,但是主要面向中大型企业客户,目前环信平均客单价在5万左右,部分大客户客单价超过50万元。

   参考2015年Zendesk约2.08亿美元营收,以及50%以上增速,中国云客服市场仍然广阔。对于环信来讲,业务定位并不止于客服。

布局未来,重点投入人工智能

   在云客服产品推出之后,环信成立客服机器人业务线。

   随着人力成本的提高,客服将不再是一个依赖人力的行业,以人工智能为基础的客服机器人将逐渐成为客服的重要部分,不仅如此,在应对日常的客服之外,客服机器人还能分析客户画像,进行精准营销,是客服行业未来的趋势所在。

   从目前行业现状来看,云客服厂商在人工智能领域的表现都差强人意,有些仍旧是基于搜索的方式做客服机器人,结果表现为用户体验差,维护困难。

   针对人工智能,环信投入20多人的团队开发基于深度学习的人工智能机器人服务,并已推出了环信机器人、环信客户声音和环信机器人智能质检等产品功能。在人工智能的探索上走在前列。
 

A`@GX@}M18VXHV3NYAXPRB8.png



近期,爱分析对环信创始人刘俊彦进行访谈,现将部分内容分享如下。

Q:IM、云客服和客服机器人三块业务占比?

A:收入上来讲,IM与云客服一半一半,客服机器人这边有20多人的团队在做,暂时没有收入。

Q:环信在客服机器人的进展?

A:人工智能的产品建设需要很多时间,所以持续在进行,已经陆续推出几个产品,包括环信智能客服机器人、环信客户声音和环信机器人智能质检。

环信智能客服机器人为客户提供机器人客服,客户声音基于全渠道客服产生的大量非结构化数据做客户旅程的透析,环信机器人智能质检会应用在一些特殊行业比如保险、金融等监管要求比较高的行业,做全员智能质检,现在基本能检出80%的内容,很大程度上代替人工质检。

Q:做好客服机器人的关键在于?

A:我把机器人在客服行业的落地分为几个层面。第一个层面是单轮会话的能力,这一项所有做客服机器人的公司都能做,但有部分公司需要人工对问答库做手动的维护和标注。这是当前很多企业实施客服机器人失败的主要原因,客服机器人系统买的起,但长期维护成本太高,用不起。第二个层面是多轮会话的能力,可能有50%的公司做不了多轮会话。第三个层面是人机协作,在一些以销售为导向的售前场景,服务的目的是为了转化和成单。销售是一门艺术,当前的人工智能技术,还达不到代替一个topsale的阶段。这种情况下,机器人退居二线,但可以根据消费者和真人客服的对话过程向客服推荐会话,不仅能提高客服的工作效率,好的会话推荐模型还可以把行业最佳实践应用到每个普通客服的身上。

做好机器人客服,不仅要有牛逼的人工智能团队,更要有海量的数据。泰康在线,新东方都是环信机器人的客户。

Q:人工智能在客服领域的应用前景?

A:创业公司涉足人工智能一定要找到最垂直的行业,深挖下去,否则无法与Google、百度等巨头竞争。

我看到创业公司有机会的人工智能领域包括,无人驾驶、医疗、金融和客服这四个行业。人工智能之所以在客户领域落地会产生巨大价值是因为:1.客服行业是个传统的劳动密集型行业,有通过新技术来大幅度优化人力成本的强烈需求。2.客服行业天然存在海量的数据,如以语音,文字为主要形式的客服沟通记录。3.客服沟通记录中沉淀了大量的行业知识体系,比如新东方的客服如何帮助消费者推荐一个英语课程。通过机器人系统不断对特定领域的知识图谱进行抽取和积累,是阻止后来者包括巨头公司进入的主要门槛。在客服领域,金融理财、教育、电商对AI的需求非常大。

Q:环信在2014年以IM起家,当时市场的竞争情况?

A:环信是第一家正式发布即时通讯云的公司。

在2014年随着IM产品的成功,很快有几家创业公司进入,到2015年市场进入混战的格局,最多的时候有14家公司,其中包括阿里的2个产品,腾讯的1个产品,但是到2016年就变了,市场只有前三名有份额,环信排第一。

ToD市场遵循28原则,市场第一拿8,第二拿2,第三不入流,其他的公司拿不到份额就撤了。

Q:BAT在IM领域内部创业为什么失利?

A:哪怕是BAT在to B领域创业,也不一定能成功。

首先是技术门槛,虽然阿里和腾讯的微信、QQ和钉钉很强,但是内部创业的团队并一定是核心团队,相比创业公司并不一定有优势;

第二是资金门槛,BAT内部创业拿的钱也有限,可能拿个500万开始创业,但是比较环信拿了几个亿融资,并不具备优势,如果短时间内做不到行业第一,BAT也不会继续为他们输血;

第三是流量门槛。企业级服务与to C不一样,BAT在to C端的流量优势并不能为to B的业务直接导流;比如环信就从来不做电梯广告,因为那个没用。to B的用户流量是要一个一个生磕出来的,在这点上创业公司没有劣势。

第四是产品门槛。像snapchat这样的to c产品,大家都一看就懂,BAT可以用2周抄出产品,然后利用资金和流量优势快速放大。但toB行业完全不一样。一个200人的呼叫中心到底怎么管理的,现场大屏幕监控怎么做,排班怎么排,KPI怎么考核,这个没有多年垂直行业经验连产品原型都做不出来。BAT不如这个专注于垂直领域的创业公司有经验。


Q:环信客服业务面向中大型客户,IM面向的客群是?

A:IM的客群包括大客户和小客户,因为交付更简单,大型客户小型客户都能满足,只不过大的客户客单价高,小的客户是长尾数量大。

Q:云客服业务包括售前和售后?

A:环信早期偏重做售前,因为像教育、金融、电商这些付费能力强的行业最重要需求在售前,初期的大客户也集中在售前环节。而且基于环信IM的客服形式,主要强项也是实时沟通,适合要求响应速度快,客单价高的售前环节。

售后部分主要是邮件、工单这类服务,现在做好售前的基础上也开始做售后服务。

Q:环信很少对外披露自己的数据?

A:环信不是不做公关,而是针对性的做公关,因为我们的客户集中在比较传统的中大型企业,所以对外宣传的目标都是这些企业,在他们能看到的地方比如说行业媒体和会议上投放的比较多。

客户看到环信的介绍后,再了解环信,都愿意使用环信的产品,所以环信的百度指数是最高的。

Q:用户数量?

A:环信IM用户8万家,客服用户2.9万家。其中有许多是之前的免费用户,但是现在环信的策略是不再开放免费版,每一个新客户都收费。

环信客服用户增长很快,互联网属性很强,但是现在其他行业属性也很强,比如教育、金融、电商这些偏传统行业,客户客单价大几万。

Q:大客户客单价在什么水平?

A:年费50万以上属于大客户。

Q:为什么说国内这波企业级服务投资热潮是在押注中国出现千亿市值的to B公司?

A:中国一定会出现自己的Oracle和Salesforce。Oracle市值1600亿美金,Salesforce500多亿美金。

一是中国企业在软件上的消费能力正在打开,去IOE后大型企业不买IBM、Oracle、EMC,开始选用国内的产品,而中小企业也开始注重效率,愿意为软件服务付费。二是国外企业因为ICP牌照进不来中国,给了国内企业成长的机会,简单的例子就是百度能成长起来是因为Google被赶出去了。

Q:成为千亿市值的企业级服务公司的路径是什么?

A:首先要占据核心赛道。只有在核心赛道上,才能保证足够的纵深和各路竞争对手厮杀迂回,才能有足够的市场支撑足够大的销售收入并放大估值想象空间。现在我看到的核心赛道大概有五条:协同办公(但是已经被BAT占据);销售自动化(销售易,纷享销客、红圈营销。都过c轮了或者新三板挂牌了,市场格局已定,新公司难有机会);客服跑道(现在虽然环信比较领先,但也还有不少公司竞争,类似1年前的销售自动化赛道。预计1年后市场会逐渐整合,剩下三四家左右);HR跑道;财务软件跑道(被用友、金蝶占据,创业公司机会小)。

第二在核心赛道上成为小巨头;预计会享有几十亿到百亿的市值。

第三通过自身横向扩张或者并购进入其他核心赛道,成为大巨头,综合性软件公司。Oracle和Salesforce都是综合性软件公司。zendesk只做客服软件,所以现在zendesk市值只有20多亿美金。

Q:云客服竞争中脱颖而出的关键?

A:关键在服务好大客户,大客户会有大量的定制开发和个性化需求。要做到这一点就需要建设好底层的PaaS平台,环信不敢说已经做好了PaaS,但基本完成了2/3。未来80%的通用需求环信来解决,剩下20%的个性化需求企业通过PaaS平台来实现。

80%是核心,20%可以外包出去,由生态圈伙伴和集成商来做。

当然还有人工智能。人工智能决定了一年后,你只是一家工具软件公司,还是一家可以输出服务,行业知识图谱,以及行业最佳实践的公司。


收起阅读 »

见云沙龙:互联网出海生存之道

你,是否厌倦了国内互联网竞争的血雨腥风? 你,是否开始向往国外广阔市场的美好前景? 你,是否需要了解如何跨过走向海外的暗礁? 越来越多的企业希望跨出国门,探索海外蓝海市场。但国内企业出海仍然面临技术、文化、商业环境等诸多挑战。为此,见云社区、迅达云Spe...
继续阅读 »
你,是否厌倦了国内互联网竞争的血雨腥风?

你,是否开始向往国外广阔市场的美好前景?

你,是否需要了解如何跨过走向海外的暗礁?

越来越多的企业希望跨出国门,探索海外蓝海市场。但国内企业出海仍然面临技术、文化、商业环境等诸多挑战。为此,见云社区、迅达云SpeedyCloud、IBM联合发起了“见云沙龙”第3期——“互联网出海生存之道”主题沙龙,将邀请百度、大观资本、Mobvista、迅达云、IBM、白鲸社区等嘉宾参与,旨在分享企业出海的经验,避免掉进同一个坑。

11月26日,周六,北京、中关村、3W咖啡,

经验丰富的老司机们,讲讲如何驾驭出海的几座大山,技术、文化、环境、资本......来这里,和榜样们面对面,成为下一个榜样!

活动议程
13:00~13:55  签到

13:55~14:00  开场介绍

14:00~14:30  
《海外互联网产品的技术经验》
刘占亮 互联网国际化专家

在海外市场,你可能遇到技术、文化、语言等各种问题,本次分享将从以下几点介绍在海外市场运营的经验:
1、工具型产品 vs. 内容型产品
2、语言文化差异与用户需求把握
3、数据驱动 vs. 产品驱动
4、产品技术挑战

14:30~15:00    
《IBM SoftLayer 公有云 -省心易用, 全球同服》
蒋旭春 IBM云计算技术经理

IBM SoftLayer公有云如何帮助企业出海,跨境运维,全球同服,独特的竞争优势。

15:00~15:30    
《引领非洲互联网春天的混合网络媒体平台》
庞一 四达时代集团研究院常务副院长

在泛非地区(撒哈拉沙漠以南),在不同层面(骨干网、接入网、终端等),将广播电视网与互联网整合,提供更高效低成本的端到端视频直播、点播媒体传输运营平台。利用互联网技术升级付费广播电视业务的支撑能力。在泛非地区,缺少有线网络,缺少宽带网络,缺少计算机终端,只有近些年发展的移动互联网网络及增长快速的智能移动终端,本演讲会介绍针对泛非独特的互联网基础设施的环境,如何与广播电视网络结合,提供更优质的媒体服务平台,进而支持更丰富的互动增值业务。

15:30~16:00    
《全球云计算资源一体化整合实战》
陈震 迅达云SpeedyCloud运维总监

在全球化的今天,尽管大型云服务公司覆盖了大部分地区,但仍然不能保证100%覆盖。因此,将不同的云服务平台无缝的整合在一起就显得十分必要了。本次分享将介绍迅达云如何在全球整合云计算资源,并为用户提供一体化的服务的实践经验。

16:00~16:30    
《猎豹出海故事》
范路 大观资本创始合伙人

猎豹移动于2010年10月由金山安全和可牛影像合并而成,专注在海外移动市场。本次分享将介绍猎豹移动出海的历程,以及选择出海的原因和背景。在出海之后,遇到的各种问题以及解决的方法。

16:30~17:00
《海外产品推广&变现》
王平 Mobvista CTO

2014年猎豹在美上市,将中国移动出海推到了崭新的里程碑高度,吹响了大航海时代高速发展的号角,至2016年,中国移动产品在海外已经百花齐放。出海已成热点,而出海初期如何迅速获取海外用户,当用户规模起来后又如何解决变现,始终是开发者关心的问题。

17:00~17:30  圆桌讨论《互联网出海生存之道》  

17:30    结束
 
讲师介绍


刘占亮.jpg


刘占亮 互联网国际化专家

刘占亮,天津大学计算机硕士。毕业后先后供职于微软亚洲研究院、腾讯、搜狗和百度,历任研究软件工程师、研究员、资深研发工程师/高级技术经理。目前在百度国际化事业部负责内容型产品线的算法和架构团队。2006-2010曾作为产品技术负责人参与创办Hitchsters.com。同时他与学术界也有密切的联系,他是中国人民大学大数据分析与智能实验室的兼职架构师。他有着广泛的研究兴趣,包括信息检索、互联网搜索、数据挖掘、自然语言处理和人工智能等。

蒋旭春.jpg


蒋旭春 IBM云计算技术经理

蒋旭春,IBM云计算技术经理,10+跨国运营商CP/SP/PaaS平台开发设计经验。

庞一.jpg


庞一 四达时代集团研究院常务副院长

庞一女士现任四达时代集团研究院常务副院长(主持工作),分别于2005年和2010年获得清华大学计算机系工学学士学位和博士学位(优秀),西贝尔学者(Siebel Scholar)。博士期间从事视频图像高性能处理等课题研究,作为第一作者获得视频领域优秀国际期刊IEEE Trans. CSVT最佳论文奖,这是大陆高校首次独立获得该奖项。2012年加入四达时代,2013年负责筹建四达时代研究院,设计构建四达下一代多业务平台,利用互联网技术提升广播电视媒体传播平台支撑能力,构建泛非首个媒体云平台、大数据平台和在线支付平台,拓展互联网媒体娱乐业务。

陈震.jpg


陈震 迅达云SpeedyCloud运维总监

陈震,2011年开始从事公有云服务运维工作。2011年至2013年在蓝汛就职高级运维工程师,曾维护过BTV、小米等客户。2013年中旬入职迅达云成,任职运维经理,后升为运维总监。

范路.jpg


范路 大观资本创始合伙人

范路,大观资本创始合伙人,是一位技术型的投资人,结合了10年的码农经验,和20年互联网和移动互联网经验,行业内真正熟悉出海运营的技术专家。

王平.jpg


王平 Mobvista CTO

王平就职于Mobvista,担任CTO,负责公司整体商业变现技术体系的构建和管理。Mobvista,是亚洲最大的移动广告平台,Mobvista是亚洲最大的移动广告平台,凭借着自身覆盖全球240多个国家、每天超过100亿展示量的移动流量体系,专注于海外高质用户获取与流量变现。在加盟Mobvista之前,先后在百度、高德就职。在百度,作为凤巢核心成员,负责凤巢流量变现商业产品的技术管理,主要聚焦在广告存储、变现算法和架构、百度商业知心等方向,承担百度凤巢提升10%~14%的收入业绩。随后加入高德担任商业变现总监,负责产品和技术管理。9年多的职业生涯,一直活跃于互联网商业变现领域的风口,对行业技术体系构建有深刻的理解。
 


联合主办.jpg


现场福利


现场福利.jpg


 
报名链接:http://www.huodongxing.com/event/6359813352600 收起阅读 »

有关头像处理的问题

在我们用环信这个IM功能的时候,环信这边不会个给我们处理头像的,这个其实换新已经做过指导了,有两个方式, 第一个就是缓存本地,在我们刚进入程序的时候,我们把获取所有的好友信息,把她们的信息获取到,这时候我们要知道,在我们用 self.messageArray ...
继续阅读 »
在我们用环信这个IM功能的时候,环信这边不会个给我们处理头像的,这个其实换新已经做过指导了,有两个方式,
第一个就是缓存本地,在我们刚进入程序的时候,我们把获取所有的好友信息,把她们的信息获取到,这时候我们要知道,在我们用 
self.messageArray = [NSMutableArray

arrayWithArray:[[EMClient sharedClient].chatManager getAllConversations]];

这个方法获取的是所有的好友的ID ,在我们查询数据库的时候根据这个ID 进行查询就可以获取这个用户的你所要的信息,头像,昵称,等等,在这个方法中需要注意的时候,当被人添加你为好友的时候,或者你删除被人为好友的时候,需要更新你的数据库,
第二个就很简单了,消息扩展
 NSDictionary *dic =  [[NSDictionary alloc]initWithObjectsAndKeys:@"rand",@"money_type_special",self.userID,@"userID",self.position,@"positionStr",self.nameCatergoryStr,@"name",self.gooAtStr,@"gooAtStr",self.imagUrl,@"imagUrl", nil];

EMMessage *message = [EaseSDKHelper sendTextMessage:@"向您分享一张名片! " to:model.userid.stringValue messageType:EMChatTypeChat messageExt:dic];

[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:^(EMMessage *aMessage, EMError *aError) {

if (!aError) {

[MBProgressHUD showSuccess:@"发送成功" toView:self.view];

self.tableView.userInteractionEnabled = YES;



}else{

[MBProgressHUD showError:@"发送失败" toView:self.view];

}

把头像和昵称做成字典dic 在接受消息的时候,按照消息扩展   NSDictionary *ext = messageModel.message.ext; 进行相应字段解析就OK了
希望对你有帮助 收起阅读 »

你看过最漂亮的开源IM Demo UI

项目使用环信SDK,实现了IM基本功能,我是以极认真的态度来做这个项目,我不想仅仅做一个demo,我要在细节、完成度、bugfree上不输给任何商用APP,做一款媲美微信的IM应用。 项目开源地址:https://github.com/gyjzh/LLWeC...
继续阅读 »
项目使用环信SDK,实现了IM基本功能,我是以极认真的态度来做这个项目,我不想仅仅做一个demo,我要在细节、完成度、bugfree上不输给任何商用APP,做一款媲美微信的IM应用。

项目开源地址:https://github.com/gyjzh/LLWeChat
github上有项目的详细描述,这里就仅仅发一部分,下面上效果图。

一、输入框动态调整高度

输入框动态调整高度.gif




二、文本全屏浏览

文本全屏浏览.gif




三、发送照片

照片发送.gif




四、发送视频

视频发送.gif




五、地图

地图1.gif




六、录音时长

录音最大时长.gif




这个项目的顺利开发离不开环信的支持,在此祝愿环信越来越好。 收起阅读 »

【视频】刘俊彦-下一代客户服务软件的三大核心驱动力

 视频地址http://yuntv.letv.com/bcloud.swf?uu=b133c59dd0&vu=23903c2699&pu=adfb3ced5e&auto_play=1&gpcflag=1&lang=zh_...
继续阅读 »
 视频地址http://yuntv.letv.com/bcloud.swf?uu=b133c59dd0&vu=23903c2699&pu=adfb3ced5e&auto_play=1&gpcflag=1&lang=zh_CN 收起阅读 »

解读Gartner 2017年十大技术趋势

   近日Gartner公司着重介绍了在2017年对多个组织具备战略意义的重大科技发展趋势。分析师在座无虚席的Gartner ITxpo座谈会介绍了他们的发现。这次发布的十大技术趋势报告,可以总结概括为:以智能为中心,通过数字化实现万物互联。   ...
继续阅读 »
   近日Gartner公司着重介绍了在2017年对多个组织具备战略意义的重大科技发展趋势。分析师在座无虚席的Gartner ITxpo座谈会介绍了他们的发现。这次发布的十大技术趋势报告,可以总结概括为:以智能为中心,通过数字化实现万物互联。
 

640[2].jpg



   Gartner将战略科技发展趋势定义为具有颠覆性潜力、脱离初级阶段且影响范围和用途不断扩大的战略科技发展趋势,这些趋势在未来五年内迅速增长、高度波动、预计达到临界点。

“2017年十大战略科技发展趋势为Gartner智能数字网(Intelligent Dgital Mesh)提供了舞台。”Gartner副总裁兼研究员David Cearley说,“这十大趋势中,前三个趋势体现了‘智能无处不在’,数据科学技术和方法如何演化,向先进的机器学习和人工智能发展,以将智能物理和基于软件的系统应用于机器学习和自适应。接下来的三个趋势聚焦数字世界,物理和数字世界日益纠缠。而最后四个趋势则着眼于智能数字网络所需的平台和服务网络。”
让我们逐一看看Gartner预测的十大技术趋势并进行分别简单地分析。
 
人工智能和高级机器学习


   人工智能(AI)和高级机器学习(ML)由许多科技和技术(例如,深度学习、神经网络、自然语言处理(NLP))组成。 更先进的技术超越了传统的基于规则的算法,以创建理解、学习、预测、适应和潜在地自主操作的系统。 这就是智能机器出现“智能化”的原因。

   “应用人工智能和高级机器学习带来了一系列智能实现,包括物理设备(机器人,自动车辆,消费电子)以及应用和服务(虚拟个人助理(VPA),智能顾问)”。Cearley说,“这些实现将表现为新一类的智能应用和物件,以及为广泛的网格设备和现有的软件和服务解决方案提供嵌入式智能。”



这个问题的关键在于如何产生嵌入式的AI技术,能够支持业务流程、模型,以及从一开始就被定义为企业中“智能”模块的应用。另外,资金也是个大问题。

智能App


   诸如VPA的智能App执行人工助手的一些功能,使日常任务更加容易(例如,优先考虑电子邮件)及其用户效率的提高(突出最重要的内容和交互)。其他智能App(如虚拟客户助理(VCA))更加专注销售和客户服务等领域的任务。因此,这些智能App有可能改变工作的性质和工作场所的结构。

   “在未来10年,几乎每个应用程序和服务都将包含一定水平的人工智能。”Cearley说,“这将成为长期趋势,不断发展和扩大人工智能和机器学习应用和服务。”



   这对一些企业而言是一个巨大的机遇,目前在应用、基础设施和设备上的智能分量越来越重。运用智能应用的形式是通过新的智能特性嵌入到某一行业的现有应用程序中。麦当劳生产汉堡包运用了智能App,对汉堡包面包生产的监控由人工专为自动化,能够提高并保证质量。智能应用还拥有数据和分析的优势,如,每分钟通过照片分析超过1000个面包来检查颜色、形态和芝麻分布,并根据分析结果不断自动调整烤箱和流程,以保证生产最好的汉堡包面包。其带来的结果显而易见:避免了浪费,每年节省数千万元;加速生产并节约能源;减少人工成本。
 
   智能应用对其他行业也有显著的价值,比如医学顾问。凯特琳癌病中心肿瘤学顾问,应用智能App可带来高价值的用户,并能解决复杂的问题,整理杂乱、非结构性和重复内容,并带给用户高影响力的结果。
 
智能事物


   智能事物指的是超越刚性编程模型的物理实体,利用人工智能和机器学习来实现高级行为,与周围环境和人们交互更加自然。作为智能事务,如无人机,自动车辆和智能家电的不断普及,Gartner预计单独的智能物件将演变为协作的智能物件模型。

   智能事物的预测,就是认为在可以预期的未来,任何事物都可以智能化,比如消费者产品、医疗产品、嵌入式产品等等。但这个预测已经滞后了,现在已经有这方面的产品,甚至都具备了可用性。比如Google、Uber研发的应用在通用道路的自动车辆;运用在特定环境的自动驾驶汽车:如无人收割机等;以及各种各样的机器人,比如零售机器人、迎宾机器人等等。



除了上述智能事物的形式外,还有协同智能。

虚拟和增强现实


   沉浸式技术,如虚拟现实(VR)和增强现实(AR),改变了人与人和人与软件系统交互的方式。
 
    Cearley说:“到2021年,沉浸式消费、商业内容和应用程序的格局将发生巨变。VR和AR功能将与数字网络合并,形成一个更加无缝的设备系统,能够协调来自用户的信息流作为超个性化的相关应用和服务。融合多个移动、可穿戴设备、物联网和大量传感器的环境将拓展沉浸式应,超越孤立的单人体验。客房和空间将与物体互动,它们通过网络的连接,并与沉浸式虚拟世界一起工作。”



时下,正是VR、AR兴起的时代。有各种各样的产品已经出现并投入使用,比如可以应用于模拟体验;对于科学研究来说,可以用于分子建模;医生也可以用于远程虚拟诊断并开出医疗处方,等等。孩子还可以应用这种技术,进行探索动物园等未知世界。这个趋势的预测已经滞后了,严格讲,不算趋势预测而是实实在在来临了。
 
数字化双生


   数字化双生(Digital Twin)是事物或系统的多元软件模型,它依赖于传感器数据来理解其处境,响应变化,改进操作和增加价值。数字化双生包含元数据(例如,分类、组成和结构),处境或状态(例如,位置和温度),事件数据(例如,时间序列)和分析方法(例如,算法和规则) 。

   在三到五年内,数以亿计的物件将由数字化双生的形式呈现。组织将使用数字化双生主动修复和规划设备服务、规划制造流程、操作工厂、预测设备故障或提高运营效率,以及执行增强的产品开发。因此,数字化双生最终将成为技术人员和传统监测设备和控制(例如,压力计,压力阀)的组合的代理。



   这个预测没有什么新意。显然,物联网的发展显示了数字化双生的潜力。数字化双生可以理解为不断发展和扩张的数字化宇宙,这需要从智能生态系统的内容、广度和控制将继续从不同维度上扩展。

区块链和已分配分类帐


   区块链(Blockchain)是一种已分配分类账(Distributed Ledgers),其价值交换交易(以比特币或其他代币计)按顺序分组成块。每个块链接到前一个块,使用加密的信任和保证机制,跨对等网络进行记录。区块链和分布式分类账概念日益广受关注,因为它们有望改变行业运营模式。虽然当前炒作是围绕金融服务行业,但是应用前景广泛,包括:音乐分发,身份验证,所有权登记和供应链。

   Cearley说:“已分配分类帐有可能引起一场变革,但大多计划仍处于早期的Alpha或Beta测试阶段。”


   该趋势预测的要点就是在不可信环境中增加信任的机制,对于重要数据和事件不可更改的记录,例如货币交易、财产登记或其他有价资产,不仅是被动式数据记录,同时能够有选择性的为事件增加动态预置行为。这个技术在供应链中蕴含着万亿美元的机遇。但是人们并不十分了解区块链技术,最大的挑战就是如何能广泛使用区块链。区块链的实施需要各方进行深入合作,区块链行业将如何发展,现在还无法预测是各方割据还是一家独大的局面。

会话系统


   会话界面的当前焦点集中在聊天机器人(chatbots)和支持麦克风的设备(例如,扬声器、智能手机、平板电脑,个人电脑和汽车)。然而,数字网格有一系列不断扩展的端点,人们可通过这些端点访问应用程序和信息,或与他人。社交群体、政府和组织互动。随着设备网格的演变,连接模型将会扩展,设备之间出现更大的协作交互,为连续和环境数字新体验奠定基础。


   现在已经有了成熟的方案,比如苹果的Siri,微软的Cortana、京东的小i,这类应用的共同特征是通过智能云服务,聊天机器人和个人助理成为对话媒介。甚至进入了婴幼儿产品市场,如腾讯的早教故事机米小兔,就是通过智能云服务来达到人工智能对话的。语音对系统界面很重要,越来越智能、越来越交互的语音接口,将是人机交互的首选方式。未来的交互方式是一种主动性对话,将进一步增强语音交互。主动式语音协助由用户位置等数据进行支持,将会在没有人机交互的情况下,也能够提供指导和管理。随着语义分析的加重,会话语音的价值会增加。语音分析能够理解用户服务记录和社交媒体的各类数据并进行推理。

网格应用和服务架构


   在网格应用程序和服务架构(MASA)中,移动应用程序、网络应用程序、桌面应用程序和物联网应用程序链接到广泛的后端服务网络,创建被用户视为“应用程序”的内容。该架构封装服务,并在多级别和跨组织边界公开了API,从而平衡了服务的灵活性和可伸缩性的需求与服务的组合和重用。
    网格使用户能够具有针对数字网格(例如,桌面、智能手机、汽车)中的目标端点的优化解决方案以及当他们在这些不同信道上切换时的连续体验。


这只是基于理想状态的市场竞争中设想的。现实是,在非理想的市场竞争中,专有基础架构数量并不占优,要实现所有的软硬件基础架构无缝集成并协同,难于上青天。

数字技术平台


   数字技术平台为数字业务提供基本的构建模块,是数字业务的关键推动者。Gartner已经确定实现数字业务新功能和商业模式的五个要点:信息系统、客户体验、分析和智能、物联网和业务生态系统。 每个组织都将有这五个数字技术平台组合的平台。

   可以理解为对话式AI驱动平台。


   比如亚马逊的amazon echo+wink。物联网平台是解决方案成功的必要因素,现在微软Azure、ORACLE、Google、SAP等IT巨头都有涉足。惠顾上世纪60年代,是主机系统,到70年代就是微型计算机,80年代是PC和文件共享LAN,90年代是服务器和图形界面,1998~2007年是因特网和网络应用,2007~2016年是移动和云,未来,将是对话式AI和物联网。所有这些并非割裂,而是互相交织。未来会有越来越多的基础架构和应用程序融合,组织需要了解这种融合,对他们的竞争力、应变能力带来什么样的影响。

自适应安全架构


   智能数字网格和相关的数字技术平台和应用程序架构创建了一个日益复杂的安全世界。 Cearley表示:“规定的安全技术应作为确保物联网平台的基准。监控用户和实体行为是物联网中特别必要的一个重要补充,然而,物联网边缘是许多IT安全专业人员产生薄弱领域的新前沿,因此经常需要新的补救工具和流程,建立物联网平台必须考虑这些因素。”



   自适应安全架构能够实现持续分析用户和实体行为,如外部身份和威胁智能,包含审查、规则/评分;持续画像;持续分析和验证。但是通常业务端会滞后于技术端,而技术端又滞后于设备和应用端,还要对安全行业有一个正确的认识——本质就是响应机构,是以解决问题为目的,而不管这个问题出现有多久。 收起阅读 »

千人盛会——2016永洪科技用户大会

智慧运营 数造未来——永洪科技•2016用户大会 一、大会背景 大数据经济时代,数据分析支持企业决策,数据分析产品层出不穷,数据分析技术与行业应用渐成各行业追逐热点。在IT产业前沿北京,金融、制造、交通、能源、医疗、政企、越来越多的企业开始在大数据领...
继续阅读 »
智慧运营 数造未来——永洪科技•2016用户大会

封面.jpg


一、大会背景
大数据经济时代,数据分析支持企业决策,数据分析产品层出不穷,数据分析技术与行业应用渐成各行业追逐热点。在IT产业前沿北京,金融、制造、交通、能源、医疗、政企、越来越多的企业开始在大数据领域创造企业价值的创新与发展。
11月26日,永洪科技齐聚“政企学协资社用”生态七方,携一站式大数据分析平台产品及行业经典应用方案,力邀1000名各行大数据行业专家与来宾,共同探讨大数据的生态应用,一场史无前例的大数据峰会等待着您的莅临!

二、大会概况
峰会主题:“智慧运营 数造未来“——永洪科技•2016用户大会
峰会时间:2016年11月26日 9:00—18:00(周六)
峰会地点:北京千禧大酒店(北京市朝阳区东三环中路7号院5)
峰会规模:1000+人    
峰会人群: 
* CEO、CIO、 CMO、 BI、数据分析、数据挖掘、大数据平台等领域开发人员、技术经理和总监、数据分析爱好者等;
*制造、电商、零售、金融、互联网、电信、能源、交通、医疗、政企、渠道等各行业业务部门信息化负责人;
*IT服务商、渠道商、系统集成商、代理商等;

三、会议议程

会议议程.jpg


四、会议亮点
    趋势解读:大数据成为国家战略的今天,数据已然成为企业的无形资产,如何构建企业自己的一站式大数据平台,顶级行业专家将为您解读其中的奥秘。
    新品发布:“从v1.0到v7.0,永洪科技的大数据分析平台到底实现了哪些创新和突破?“——十年磨一剑,永洪科技如何自主创新实现蜕变,且听创始人Henry娓娓道来。
    尖峰对话:“这么多数据,我该如何挖掘其中的价值?数据化运营如何开始、如何构建体系架构... ...”灯不拨不亮,理不说不明,行业精英共同探讨如何实现数据驱动智慧企业。
    同行取经:制造、金融、能源、交通、互联网、电商、政府、医疗等行业用户携现身说法,讲解自身痛点、解决方案及数据分析的应用价值,让你少走弯路少进坑。

五、报名方式
抢票链接:http://www.bagevent.com/event/267090

六、合作单位
主办:永洪科技•永洪数据科学研究院
协办:环信、中关村大数据产业联盟、首席数据官联盟、崔牛会、天善智能、CDA数据分析师、msup、爱数据网、数据分析网、数据猿、China Hadoop Summit、Bi168大数据社区、大数据人、segmentfault、ThoughtWorks、数董会、经管之家、PPV课; 
媒体:IT168、CSDN、中国软件网、中国计算机报、SP计算机产品与流通、商业伙伴、计世传媒; 收起阅读 »

第四届互联网运营大会 - 数据驱动产品运营和社会化运营

大会时间:2016年11月27日 9:00 ~ 2016年11月27日 17:00 会议地点:上海浦东新区博云路2号浦软大厦 大会规模:600人 报名链接:http://www.huodongxing.com/event/9359909288600   数据...
继续阅读 »

封面.jpg


大会时间:2016年11月27日 9:00 ~ 2016年11月27日 17:00
会议地点:上海浦东新区博云路2号浦软大厦
大会规模:600人
报名链接:http://www.huodongxing.com/event/9359909288600
 

数据驱动产品运营和社会化运营

8位重磅嘉宾分享互联网运营经验和方法!
由中国最大的产品经理社群PMCamp和汇智Tek联合主办的第四届中国互联网运营大会((IOCon2016)将于2016年11月25日-27日在上海浦软大厦举行。
本届大会主题为: “数据驱动产品运营和社会化运营”。大会将邀请来自互联网、传统行业、服务业、新型创新企业的CEO、COO/产品副总裁/产品总监/产品经理、运营副总裁/运营总监/运营经理,创业者等,围绕大变革时代的各种机会和问题,探讨产品商业模式、产品运营、数据分析、社交媒体运营、社会化运营、游戏化设计和运营,提供产品运营的道、术、器等内容分享,为一切关注产品创新、产品运营、互联网创业者搭建一个学习交流、思想碰撞的干货分享平台。
自2010年起,PMCamp已经举办过六届产品经理大会,三届互联网运营大会、两届数字营销大会、五届人本设计大会,以及近百场的产品经理工作坊、设计工作坊、产品经理沙龙等线下聚会活动。正在筹办互联网产品增长大会。5年来,已经有超过10万人关注PMCamp,1万多人参加过PMCamp的活动,600家企业代表参加过PMCamp组织的活动
 
演讲嘉宾

子墨.jpg


子墨
前阿里巴巴运营专家,
Convertlab 联合创始人,多准数据CIO
 
子墨,前阿里巴巴运营专家,新零售人社群创始人,独立传统行业转型互联网实战顾问,浙江大学、上海交大、淘宝大学授课嘉宾; Convertlab 联合创始人;多准数据CIO;新零售人社群创始人;老阿里茶馆/漫私塾咖啡创始人;
曾在微软金融事业部从事IT服务顾问、在阿里巴巴五年,是淘宝礼物创始人,阿里旺旺卖家版.运营专家。曾作为 SAP、有赞、商伴等软件公司资深业务顾问,以及TCL、安奈儿、平安集团、等企业互联网业务顾问。
演讲主题:新零售之全渠道用户运营解析
主题简介;传统行业如何玩转线上?电商行业如何拥抱线下?线上线下的融合不只是是1+1,而是在底层“数据”结合的基础上围绕“消费者价值”做改造,表现出来就是全渠道用户运营。
 


路盛华.jpg



路盛华
沪江高级运营总监
前盛大创新院多产品运营负责人
路盛华,沪江高级运营总监,对商业市场有浓厚兴趣,熟悉IT和互联网领域,擅长市场运营及营销管理。
曾就职于韩寒投资的去动网络科技有限公司,担任「去动」&「Fit」APP 的市场运营总负责人。曾在盛大创新院担任Bambook电子书合作推广部门负责人,负责Bambook的市场合作、渠道拓展、媒体公关、销售、运营等工作。后期在麦库记事担任产品运营负责人。
曾供职于CBSi中国(哥伦比亚广播公司)传媒集团,担任业务群组总监。期间策划主持过大型互动活动和产业发展高峰论坛。
演讲主题:运赢 :透析数据做最有效的产品运营
主题简介:数据是产品运营工作的基础,也是产品运营的目的。如何透彻的设计和分析数据,是决定成败的关键因素。
 


杨文雅.jpg



杨文雅(特工先生)
淘宝大学明星讲师,天猫国际专家顾问
杨文雅(特工先生),淘宝大学明星讲师,天猫国际专家顾问。上海飞域网络科技有限公司/上海天媒实业有限公司 总经理。电商行业实操八年以上经验,知名电商讲师,培训授课超过10万人次。擅长无线端运营、互动营销、移动电商操盘。2015年度淘宝大学最高奖项春蚕丝语奖(全国仅10人)、鲁班奖(全国仅2人)获得者。受邀给美国梅西百货、日本富士拍立得、苏泊尔、春水堂、361度等几十个大型企业、几千名各地政府领导讲课。
他也是天猫黄金台诊断高级专家,阿里数据学院认证讲师,阿里巴巴商学院出版《电商运营》书籍作者,编写组组长,百年天猫2015年双11千里马导师。
演讲主题:《电商运营新思维》
主题介绍:1.无线端趋势 2.消费结构升级带来的电商机会 3.场景化营销 4.以未来为因的运营思维
 


曾艳琦.jpg



曾艳琦
洋码头高级运营总监
前腾讯QQ市场总监
曾艳琦,洋码头高级运营总监。前腾讯手机QQ市场总监,前盛大创新院研究员,曾在11年试水移动互联网社交创业,和团队一起做了国内最早的私密社交app–“时光流”。十年互联网经验,09年进入移动互联网,尤其擅长社交类移动互联网产品。
主要工作经历:
腾讯—QQ:负责手机QQ、QQ开放平台项目QQ互联、QQ商业化项目腾讯课堂、兴趣部落、QQ群
创业-时光流:中国的私密社交应用,主打照片分享,11年-12年在中国照片类应用中市场占有率排第五;
盛大:盛大网盘everbox和中国最早的LBS社交产品盛大切客客户端;
 


洪弘.jpg



洪弘 (花名红利)
挖财理财运营总监
 
洪弘 ,挖财理财运营总监,花名红利。主要负责挖财理财的用户运营、数据运营、品牌运营、内容运营、活动运营、金融产品运营等多个团队。在用户精准营销、用户教育、转化率运营、创新产品孵化、金融产品营销等方面有丰富的经验。
 


黄捷.jpg



黄捷
携程国际业务事业单位运营经理
 
黄捷,携程国际业务事业单位运营经理,负责产品供应产品转化用户运营数据分析工作。
演讲主题:漏斗图的两个实际工作案例
主题简介:数据运营最常用的漏斗图,基础虽然简单,应用方式很多样。介绍两个中阶的应用案例,一是时序图辅助横截面漏斗图,适用于跟踪事件节假日发布活动的影响,寻找改进机会和评估改版效果,二是客户群分漏斗图,国际化网站可以通过不同地域语种划分方式,漏斗图对比找特征和流失点,这种模式也可以扩展到更多用户属性的ab测试,普适性很强。
 


王小毅.jpg



王小毅
浙大管理学院市场营销学系副主任、副教授、博士生导师
浙大管理学院市场营销学系副主任、副教授、博士生导师,中国管理科学与工程学会副秘书长,国际知名的脑神经营销专家,研究领域为互联网商业模式,移动和在线消费心理。
培训课程:“新媒体运营和内容运营”
培训简介:
- 微信公众号运营方法论和技巧
- 新媒体运营
- 平台内容运营策略
- 内容运营方法论。
 


陈世欣.jpg



陈世欣
昭合投资合伙人
前美国Movoto中国总经理
陈世欣,昭合投资合伙人,InnoSpace特聘创业导师,前美国Movoto.com中国总经理,互联网社群专家,具有10多年的增长黑客经验,3年以上用游戏化思维运营社群和组织大会经验,曾经为4家互联网企业实现高速增长。他从1999起参与多家互联网创业公司,历任任安家网副总经理、movoto.com中国公司总经理、和家网CTO、2Style4You公司CTO、永泰红磡养老产业集团战略研究总监、创猿投资董事合伙人等;
曾作为平安大学、苏宁大学、中国移动、中国银联、中国电信、复旦大学MBA教研组、众戴金融等机构内训讲师。
培训课程:“如何炼成增长黑客,如何获取用户、促活和转化价值”
培训简介:介绍增长黑客的基础和体系,系统化提出做增长的100个方法和案例,引导分组练习获取用户、促活和转化价值。
 


刘振华.jpg



刘振华
原笔记侠运营总监,新周刊特约编辑,单篇百万+作者
刘振华,原笔记侠运营总监,新周刊特约编辑,写作过单篇100W+阅读量原创文章。对知识类内容付费有深入研究。个人微信公众号有一定业界影响力。
培训课程:“社群运营和价值变现”
培训简介:
如何运营好社区和价值变现
如何策划和引爆一场活动
 
培训课程

11月25日-26日:“硅谷堂”首席运营官训练营三期
地址:上海静安区南京西路1376号上海商场曼彻斯特大学中国中心
11月25日:
09:00 ~ 12:00  《如何炼成增长黑客》,《如何获取用户、促活和转化价值》陈世欣 昭合投资合伙人,前美国Movoto中国总经理
13:30 ~ 17:00 《微信公众号运营方法论和技巧》,《新媒体运营》王小毅 浙大管理学院市场营销学系副主任、副教授、博士生导师
11月26日:
09:00 ~ 12:00  《平台内容运营策略》,《内容运营方法论》王小毅 浙大管理学院市场营销学系副主任、副教授、博士生导师
13:30 ~ 17:00 《如何运营好社区和价值变现》,《如何策划和引爆一场活动》刘振华 原笔记侠运营总监,新周刊特约编辑,单篇百万+作者
12月10日 ~ 11日: " 互联网人的数据运营实践培训课"
地点:全季酒店新天地店
12月10日:
09:00 ~ 12:00 《数据驱动渠道优化》 安琦,携程无线营销产品负责人
13:30 ~ 17:00《数据驱动设计优化》  陈抒,上汽车享网UED总监
12月11日:
09:00 ~ 12:00 《互联网数据分析师的成长之路》纪杨,Teambition数据分析负责人 
13:30 ~ 17:00 《产品运营的数据事件分析实务》黄捷,携程国际多语言网站及APP运营负责人
 

大会议程
全天共有8个演讲,1个互动论坛,1次午餐,2次茶歇
上午日程
08:20 - 09:00  签到
09:00 - 09:20  开场演讲和日程介绍   大会主持人
09:20 - 10:05  演讲1 《新零售之全渠道用户运营解析》前阿里巴巴运营专家,Convertlab 联合创始人,多准数据CIO 子墨
10:05 - 10:50  演讲2 洋码头 高级运营总监 曾艳琦
10:50 - 11:00  茶歇
11:00 - 11:50  演讲3 《电商运营新思维》杨文雅(特工先生),淘宝大学明星讲师,天猫国际专家顾问
11:50 - 12:00  演讲4 《漏斗图的两个实际工作案例 》黄捷,携程国际多语言网站及APP运营负责人
12:00 - 13:00  午餐
下午日程
13:00 - 13:45  演讲5 《数据化运营与用户生命周期管理》洪弘 (花名红利),挖财理财运营总监
13:45 - 14:30  演讲6 《运赢:透析数据做最有效的产品运营》路盛华,沪江高级运营总监,前盛大创新院多产品运营负责人
14:30 - 14:45 茶歇
14:45 - 15:30 演讲7 《游戏化产品设计和运营》,陈世欣 昭合投资合伙人,前美国Movoto中国总经理
15:30 - 16:15 演讲8 确认中
16:15 - 16:55 全场嘉宾 Q&A 互动环节
16:55 - 17:00 合影、抽奖
注意:大会议程将根据当天实际情况进行轻微调整,最终解释权归主办方所有。
购票政策
折扣票购买方式、范围、期限
1. 购买“互联网运营大会”参会票
在11月01日-11月11日期间 以199元购早鸟票 
在11月11日-11月20日期间 以299元购特惠票
在11月21日-11月27日期间 以399元购普通票
 
2. 购买“硅谷堂首席运营官训练营”培训票
全价票:2299元/人
早鸟价:早起的鸟儿有虫吃,立刻报名享受早鸟价1999元
(仅限30席,截止日期:11月11日)
 三人团购价:三人行5097(1699×3)一起学起
(费用包含课程资料费、场地费用、结业证书以及两餐午餐、课间茶歇)
注:本培训由“硅谷堂”组织,PMCamp协办。
  收起阅读 »

盘点10款逆天级效率工具,能帮创业公司节省50%时间成本

盘点10款逆天级效率工具,能帮创业公司节省50%的时间成本 在当今移动互联网创业环境里,好的创业项目在半年至一年时间里应该可以拿到A轮了。既然你是一个创业公司,要么快速“干”起来,要么赶紧去“死”,半死不活的运行着浪费公司所有人的时间,毕竟时间是创业最大的敌...
继续阅读 »
盘点10款逆天级效率工具,能帮创业公司节省50%的时间成本
在当今移动互联网创业环境里,好的创业项目在半年至一年时间里应该可以拿到A轮了。既然你是一个创业公司,要么快速“干”起来,要么赶紧去“死”,半死不活的运行着浪费公司所有人的时间,毕竟时间是创业最大的敌人。
越来越多的公司和个人也开始愈加注重效率的提升。工欲善其事,必先利其器。现在就结合互联网创业公司的实际工作场景和需求,给大家分享一些能大大提高工作效率的工具神器,这些工具能从梳理工作思路、团队协作、产品原型图设计、数据统计分析、销售与客服等各方面提高创业公司的工作效率,降低运营成本。

一、梳理工作思路,思维导图工具助你一臂之力

在日常工作中,我们经常需要梳理和展示自己的工作思路和规划,或是向领导汇报,或是向同事展示,亦或是仅供自己查看。借助思维导图工具不仅可以更高效地进行梳理,同时还能将自己的思路规划更清晰美观地展示出来。平时用得比较多且感觉不错的的思维导图工具主要有两款,MindManager和XMind。百度脑图也曾用过,但最终因为产品体验问题而不得不忍心放弃。


10期_相关推荐.jpg




先介绍下MindManager。不管是梳理运营策略、做项目管理还是开展头脑风暴,都可以在MindManager上高效完成。在MindManager上,你可以在单一视图里将想法和灵感以清晰的思维导图形式记录并展示出来。它的时间轴布局功能可帮你清楚展示产品路线图、项目里程碑或是营销发布计划。此外,你还可以利用它提供的图像库在导图里添加丰富的视觉效果。
MindManager产品官网:http://www.mindmanager.cc/

XMind.png



再分享一下XMind这款产品。XMind产品外观整体比较清新简洁,对其他格式兼容性较好,提供甘特图、矩阵图等多种结构。XMind以画布概念管理,方便思维导图的层次化。XMind可以导入MindManager、FreeMind数据里面的文件,也可以将文件导出为Word、PPT、PDF、图片和TXT等格式的文件,以便将用XMind绘制的图与他人轻松共享。
XMind产品官网:http://www.xmindchina.net/
上面两款思维导图工具都非常不错,如果你需要一款思维导图工具来协助自己工作的话,不妨直接在上面两款工具中选择一款。

二、团队高效协作是降低时间成本的有力保证

创业公司员工规模相对较少,理论上应该比较容易沟通和协作。有需要沟通协作或是需要什么东西,直接当面沟通或是在面积不大的办公室大喊一声就能搞定。然而,如果在创业公司工作一段时间,你便会发现,创业公司内部要想实现真正的高效协作其实并非易事。
大公司协作有大公司协作的繁琐,创业团队协作有创业团队协作的痛点。下面就结合创业公司的三种不同协作需求推荐三款不同类型的协作工具。
团队文档协作工具:WPS云文档

zuixin.png



文案校稿时,经常需要为往返发送稿件和上传下载而苦恼;企业资料混乱存于各处,需要时找不到;办公资料全在公司电脑里,无法随时随地办公。如果你也在为这些问题苦恼,说明你还没有找到合适的工具。
WPS云文档是一个企业文档存储、共享与协作平台,支持多人同时编辑一个文档、文档内评论和历史还原等众多便捷功能。不管你在任何时间、任何地点、使用任何设备,只要打开云文档就能处理工作文件。不管是产品研发、市场运营和人力行政人员,都可以在WPS云文档上便捷地进行文档的协作、存储与共享。WPS云文档有企业级管理后台,可管控所有企业成员和文档的权限。此外,WPS云文档还提供WPS Office全套办公组件,大家在一个平台上就能搞定所有工作。
WPS云文档产品官网:https://drive.wps.cn/ 

想用一个工具就解决工作中的所有沟通问题,试试零信


零信.png



工作中用的软件太多太乱,通知信息到处都是。这时你可能也会希望有一款一站式的企业内部沟通平台。如果你有这个需求,零信或许是一个不错的选择。
零信的是一款便于整合信息的企业IM产品。你在零信这一个平台上就能处理基本所有工作信息,让大家更专注地工作,目前支持群聊、私聊、应用接入、文件管理、视频会议等。它还支持企业自定义开发,一行代码就能搞定。零信已整合邮件、任务管理、文件管理、客服支持、代码开发等60多种应用。
零信产品官网:https://pubu.im/ 

三、改善客服、优化销售管理流程,这两款工具值得一试

无论哪一家创业公司,产品做出来之后,不管是做客服还是做销售,都需要和客户打交道。这样才能更好地了解用户的需求,并从用户那里获得收入以求生存。为更高效地为客户提供客服服务,同时优化销售管理流程,这当然离不开专业的销售和客服类工具的支持。

销售易CRM


销售易.png



销售易CRM是销售管理类工具中非常不错的一款,它以销售管理为核心,基于销售管理全流程,结合合作伙伴管理、市场与客户服务管理,可以为客户提供灵活的配置机制,帮助企业数字化销售管理,提升团队业绩和效率。销售易CRM,已实现SaaS和PaaS的无缝整合,可快速响应客户个性化、定制化的业务诉求,支持不同规模企业的销售流程。
销售易CRM产品官网:http://www.xiaoshouyi.com/ 

环信移动客服

环信.png



传统的客服工具已经无法满足移动互联网时代的需求了。这是个移动的时代,客户无处不在,客服工作极大的需要移动化和全平台化的支持。2016年双11天猫突破1111亿,其中移动端占比高达82.04%,这也是为什么在这里推荐环信移动客服的原因所在。环信移动客服支持全媒体接入,包括网页端、社交媒体客服(微博、微信)、APP内置客服和呼叫中心等多种渠道接入。全媒体客服已是大势所趋,全媒体客服核心在于移动端接入,而移动端正是环信的核心技术优势所在。

环信国内首创基于人工智能和大数据挖掘的客户行为透析产品"客户声音"可以通过自然语言解析,主题聚类,情感度分析等技术手段挖掘和分析热点话题,发现畅销或问题商品,同时分析发现服务运营中存在的问题,企业就此可以优化运营,利用大数据洞察力来发现销售机会,改善产品质量。

基于自然语言处理和机器学习技术的环信智能客服机器人,能够辅助或代替人工客服精准回答常见或高频问题,能解答80%常见问题,极大降低企业客服人力成本。目前,环信在客服领域已经服务了三万多家标杆客户,积累了人工智能在客户服务行业落地的大量最佳实践。典型用户包括国美在线、58到家、楚楚街、海尔、神州专车、新东方、链家自如客、泰康在线等众多标杆企业。

环信移动客服产品官网:http://www.easemob.com

四、做产品原型设计,这些工具肯定能满足你的需求

互联网创业公司基本都会用到产品原型设计工具。大家其实对原型有着不一样的需求,有些公司注重产品的原型版本管理,有些注重原型的交互和PRD需求文档,所以下面分享三款各具特色的产品原型设计工具,墨刀、Axure RP和Balsamiq Mockups,能够很大程度上让迭代速度上一个门槛。
首先介绍下MockingBot墨刀


墨刀.png



凭借“十分钟设计一个 App”的易用性,墨刀已经成为产品经理界非常流行的一款原型协作工具,受到知乎和爱范儿的强烈推荐。
你一定有着这样头疼的场景,导出原型后要么需要详细的文档,要么就需要非常多的沟通,和其他同事共同制作时更是频繁返工。而墨刀恰巧可以解决从原型制作-交互动效-团队协同作业-实时分析查看效果-跟踪团队及用户反馈整个产品开发流程中大部分让你挠墙的问题。
使用墨刀完成设计后,可以直接对功能进行备注及说明,简单有效。把同事拉进你的项目,就可以看到协同者的操作进程和改动细节,更好的协作和分享信息。实时的扫一扫查看,不光可以检查制作效果,更可以把原型的交互效果发送给团队和用户内测征集意见。用户可以用打点评论的方式告诉制作者他的真实感受。大大降低了产品的开发试错成本。
墨刀是一个国内的团队开发制作的产品,因此比较符合国内互联网公司的使用习惯。
墨刀产品官网:https://modao.cc/
再说说Axure RP


Axure.png



如果产品经理对产品原型、交互演示、原型细节设计等的需求比较强烈,Axure RP是非常不错的选择。Axure RP 能让产品经理快速创建应用软件和基于Web的的线框图、流程图、原型页面、交互页面和规格说明文档。Axure RP应该是目前产品经理群体中用得最广泛的一款原型设计工具。

五、要做到数据驱动,离不开数据分析产品的支持


growingio.png



对于互联网创业公司来说,因为人力和资源都非常有限,无法承受起将时间和资源浪费在没有成效的工作上,因此理想的方式是所有工作都能做到数据驱动,让数据决定该将时间精力用在什么地方。目前市面上有很多现成的数据分析产品,但并非有了数据分析产品就一定能做到数据驱动决策,如果没有更好的数据分析方法论的指导,即使有产品也未必能真正实现增长。
相较于市面上其它数据分析产品,GrowingIO背后有强大的数据分析团队,能为客户提供数据驱动增长方面的理论支持。GrowingIO是一款拥有谷歌基因的数据分析产品,因为公司创始团队基本都是来自硅谷的数据分析大牛。这款数据分析产品的最大亮点在于不需要开发人员埋点,就可以采集全量、实时的用户行为数据,不管是对管理者、产品经理、市场运营、数据分析师还是增长黑客,GrowingIO都能帮助他们实现更精细化地数据分析,从而优化产品、提升转化率,实现用户的快速增长和变现。
GrowingIO产品官网:https://www.growingio.com/ 

六、无处不在的表单设计和数据收集需求,用金数据就够了


金数据首页.png



创业公司无论是做用户与市场调研、收集意见反馈、在线投票,还是做活动报名和在线预约等,这都需要用到表单设计和数据收集工具。金数据是一款非常适合中小创业公司使用的表单设计与数据收集工具。金数据的操作非常简单,功能比较齐全,你只需通过像搭积木般的简单拖拽即可创建出好看实用的表单,高效完成数据收集工作。凡是需要表单设计和数据收集的场景,都可以借助金数据快速完成。
金数据产品官网:https://jinshuju.net/
其实能提高创业公司工作效率的工具还有很多,我在本文中只推荐了一些自己及身边同事用过且感觉不错的工具。最后还是文章开头时那句话:要么快速“干”起来,要么赶紧去死。巧用效率工具可以帮助创业公司最大限度节约时间成本,提高创业成功的概率。希望大家在工作中都能选对工具,在节省时间成本的同时,早日拿到A轮、B轮或C轮融资,甚至上市。
  收起阅读 »

从APP内直跳另一APP内容页┃Deep Linking技术探究沙龙┃11月19日(周六) 中关村方正国际大厦6楼北大孵化器

万维网颠覆传统内容架构的一点在于,它以超链形式(hyperlink)让所有内容产生了关联。但是在移动互联网中,我们几乎不可以在 app 和 app 之间随意跳转,每一个 app 都成为一个独立的孤岛。 现在Deep Linking技术将改变这一现状,实现...
继续阅读 »
万维网颠覆传统内容架构的一点在于,它以超链形式(hyperlink)让所有内容产生了关联。但是在移动互联网中,我们几乎不可以在 app 和 app 之间随意跳转,每一个 app 都成为一个独立的孤岛。

现在Deep Linking技术将改变这一现状,实现从APP内直接跳转到另一APP的指定内容页;


1.jpg


这周六,我们准备了一场技术沙龙,为你解答有关Deep Linking技术的方方面面:从Deep Linking的历史,Deep Linking 的具体形态;到Deep Linking实现原理及技术体系立体解析等等。
现邀请渴望打破app孤岛的你,一起来探究!

基本信息:
LinkedME技术沙龙第一期:Deep Linking技术探究
11月19日 中关村方正国际大厦6楼北大孵化器

13:30~14:00签到
14:00~17:00分享

分享嘉宾:
齐   坡|LinkedMECEO,清华信息技术研究院硕士,曾于新浪微博任算法工程师 擅长大数据架构,网络安全;
王仁阳|系统架构师,北京邮电大学硕士,前新浪微博Feed项目的核心开发者,具有丰富的大型互联网项目的架构经验。
徐   斌|移动研发工程师,前腾信创ios工程师和Android工程师,5年移动端研发经验,熟练掌握Android及iOS研发
郭亚伦|她理财移动开发经理,前任职搜狐新闻客户端

活动日程:
13:30
签到

14:00~14:40
打通移动互联世界——Deep Linking技术的前世今生

齐坡
14:40~15:20
Deep Linking实现原理及技术体系立体解析

王仁阳
15:20~16:00
Deep Linking在移动应用开发中的实践

郭亚伦
16:00~16:40 场景和Deep Linking应用新玩法 徐斌

16:40~17:00 Q&A+抽奖

参与沙龙,并将获得:Deep Linking技术白皮书

2.jpg



合作伙伴.jpeg


 
我要报名:http://www.huodongxing.com/event/1358649997400 收起阅读 »

Android ios V3.2.1 SDK 已发布,发起电话支持自定义内容

Android V3.2.1 2016-11-12   新功能/优化: 聊天室列表支持分页获取发起电话的接口增加ext参数,方便用户自定义内容EMOption中增加setUseHttps()的接口优化会话加载的速度Bug fix:修复使用视音频后可能导致手机外...
继续阅读 »

摄图网-桌面上摆放着笔记本和电脑.jpg



Android V3.2.1 2016-11-12
 
新功能/优化:
  • 聊天室列表支持分页获取
  • 发起电话的接口增加ext参数,方便用户自定义内容
  • EMOption中增加setUseHttps()的接口
  • 优化会话加载的速度
Bug fix:
  • 修复使用视音频后可能导致手机外放没有声音的bug
  • 修复发送消息后马上删除附件可能导致手机crash的bug
  • 修复音视频呼叫在某些魅蓝手机上会卡死的bug
  • 修复demo中呼叫时没有铃声的bug
  • 修复了视频通话时使用后置摄像头时图像显示不正确的bug
 iOS​ V3.2.1 2016-11-12 新功能/优化:
  • 聊天室列表支持分页获取
  • 发起电话的接口增加ext参数,方便用户自定义内容
  • EMOption中usingHttps默认为YES
bug fix:
  • 修复Lite版本SDK编译warning的问题

 
 版本历史:Android SDK更新日志  ios SDK更新日志
下载地址:SDK下载
收起阅读 »

环信CEO刘俊彦:SaaS领域一定能诞生千亿市值的科技巨头

   提到客服这个词,人们第一印象可能是雪白衬衣、黑亮西服,甜美声线,端庄大方的业务员耐心接听电话的情景。没错,这是典型的人工客服场景,但随着信息技术不断发展,客服内涵更加丰富,文字、语音、视频以及人工智能开始结合,客服已经成为一项重要的企业服务内容,催生出新...
继续阅读 »

renwu-liujunyan.jpg


   提到客服这个词,人们第一印象可能是雪白衬衣、黑亮西服,甜美声线,端庄大方的业务员耐心接听电话的情景。没错,这是典型的人工客服场景,但随着信息技术不断发展,客服内涵更加丰富,文字、语音、视频以及人工智能开始结合,客服已经成为一项重要的企业服务内容,催生出新的产业市场。

   环信就是客服产业中的一份子,从2013年成立到去年拿到1250万美元融资,随着市场潜力不断被释放,环信业绩也大幅增长,根据易观智库的数据显示去年环信已占据国内SaaS移动端客服市场77.4%的份额,在市场竞争中取得了不俗表现,但在新技术不断转化落地,竞争日益激烈的大环境下,如何让企业跑得更快,怎样更好地理解市场,行业天花板在哪里,作为客服领域参与者和受益者,环信CEO刘俊彦给出了他的理解。

   亿欧:企业级服务赛道很多,为什么选择客服这个领域呢?

   刘俊彦:企业服务可以分为5个核心赛道, 协同办公、CRM、客服、人力资源和财务 ,选择从客服切入首先是因为我们对这个行业了解,有过多年相关产品的开发经验,其次从市场角度来看,无论是北美市场还是目前的国内市场,客服的市场规模最大,仅国内在线服务和电话服务两个细分市场就已达到300亿元规模,像京东、阿里、苹果都在客服项目投入了巨大资源,从人员培训到系统的购买等,所以这是一项成本中心,是企业与客户沟通的第一窗口。

   从其他赛道来看,协同办公是基础性战略服务,是腾讯、阿里这样巨头的必争领域,这个领域竞争很激烈;客服是CRM中占比最大的服务内容,综合性CRM企业有纷享销客、销售易和红圈营销等;人力资源领域有北森,也是一块大市场;财务方向主要是传统IT企业例如金蝶、用友等公司。环信选择在客服深耕是从主观和客观两个维度来判断,我看好这个赛道。

   亿欧:很多客服公司都希望做中国的Zendesk,而您希望企业能有更大格局,能成为salesforce、微软和甲骨文这一类综合性公司,关于行业天花板您怎么看?

   刘俊彦:与美国相比,中国企业面临着难得机遇,眼光应该看得更远,只要把握机会,成为一家百亿美元市值的综合性软件巨头是很有希望的。

   中美两国市场环境是不同的,Zendesk在美国还不是最大客服企业,占据第一阶梯的仍然是巨头,去年salesforce的客服云收入大概是19.6亿美元,而Zendesk是2亿美元左右,双方差距近10倍,所以巨头在各个行业保持强势地位,让新型企业难以立足。其他巨头例如微软、甲骨文也有自己的客服云系统,主要服务面向客单价在几十万到几百万的大型企业,所以整体来看美国客服市场是巨头的竞技场,小型服务商机会渺茫。

   而在国内由于云服务落地受到限制,国外巨头不得不和中国企业合作共建数据中心,造成了市场空白,这给本土企业 休养生息并不断发展壮大自己赢得了时间。另一方面国内中小型企业发展迅速,付费意识和能力逐渐增强,进一步扩大了市场空间,延长了赛道深度。更重要的因素是国内在各个赛道都没有形成像美国一样强大的巨无霸企业,竞争状态相对平行,群雄逐鹿的程中,第一阶梯的企业都有机会成为巨头。

   亿欧:您说过环信有两场仗要打,一是客服赛道另一个是综合性软件,您认为打赢这两场仗的核心是什么,环信所处的阶段又是怎样的呢?

   刘俊彦:因为有大机遇所以有更强的愿景,站在行业发展方向来看,企业成长路径从确定方向再到逐步成长为该领域的小巨头,最后跨赛道边界,跻身综合性企业行列,这个过程就是环信将要面对的,目前我们处在第二阶段,这也是时间上最漫长,洗牌压力最大的阶段,走好就能化蛹成蝶,走不好就只能被淘汰。

   如果想在局部赛道和整个航道上打胜仗, 最关键就是产品和销售。

   销售要做好服务,让客户实现价值;产品是核心竞争力,是服务基础,在两者之上是资金投入,在资本推动下能实现快速成长,以往的软件公司靠自我造血进行研发,但在SaaS领域初期投入非常巨大,资金不足、产品化速度太慢,很难把握市场机遇,所以做好产品和销售再加上资本催化,SaaS企业就能突破局部赛道藩篱实现大航道方向地起飞。

   亿欧:环信在产品体系上有什么亮点吗?

   刘俊彦: 第一个亮点是客服移动化。 移动互联网时代,单靠PC端并不能满足客户需求,客户更多会从手机端访问页面,这是共性趋势,但环信更强调移动端客户体验,保证产品舒适性。

   第二个亮点是全媒体客服。社交渠道多元化和应用软件功能的丰富让全媒体客服成为客户新需求,微信公众号、微博、网页、电话等渠道的用户访问都能提供客服支持,同时数据可以统一记录并管理,以便更好服务用户。

   第三个亮点是客服智能化。对于企业来讲客服既是成本中心也是获客窗口,如何压缩成本,扩大获客能力,是人工智能的重要应用方向。一般客服场景中很多对话具有重复性、结构化特点,通过人工智能方式能够代替前端基础信息的交互,能节省大量人力成本,同时赋能客服人员以更大专业性,进而提升服务水平。对于有多轮会话需求的企业,我们也会提供定制化服务,以满足不同层次业务需求,但目前多轮会话受会话场景影响,比如噪音问题等,在技术上还有待突破。

   第四个亮点是客服营销化。通过客服这个窗口进行用户画像的描绘、有针对性采取互动营销手段,构筑完整闭环,打通服务链条,多维度布局产品,提供“从进到出”的全周期服务体验。

   亿欧:续约率是SaaS企业的盈利命门,环信在这方面有什么措施?

   刘俊彦:续约率高低体现了销售服务的好坏,我们在售出一套产品后并不是甩手不管而是有专业团队来帮助客户用好产品,根据反馈和意见来提升客户手中的产品价值。为了帮助客户在实践中提升产品使用效能,环信成立了客户成功与交付部,将服务从交付延伸到下次续约,实现全周期跟踪服务,从而保证续约率。 收起阅读 »

先定一个小目标,比如写一个QQ

 项目简介     本项目是即时通讯的示例项目,使用了MVP模式,集成了环信SDK和Bmob后端云,展示了即时通讯基本功能的实现,包括注册登录,退出登录,联系人列表,添加好友,删除好友,收发消息,消息提醒等功能。    使用的开源项目  BottomBarE...
继续阅读 »
 项目简介 

   本项目是即时通讯的示例项目,使用了MVP模式,集成了环信SDK和Bmob后端云,展示了即时通讯基本功能的实现,包括注册登录,退出登录,联系人列表,添加好友,删除好友,收发消息,消息提醒等功能。
 
 使用的开源项目 
  学习目标 
  •  环信SDK的集成与使用
  •  MVP模式的运用
  •  ORM数据库的集成与使用
  •  模块化思想的运用
即时通讯 IM(Instant Messaging)
允许两人或多人使用网络即时的传递文字讯息、档案、语音与视频交流。
相关产品
  •  鼻祖 ICQ
  •  国内主流 QQ 微信 陌陌 YY等
  •  国外主流 Facebook Messenger WhatsApp Skype Instagram Line
第三方服务平台 环信 

001.png

环信集成[list=1]
  • 注册并创建应用
  • 下载SDK
  • SDK的导入
  • SDK初始化
  •  so文件夹1. 放在jniLibs2. 也可以放在libs目录下,不过需要在模块下的配置文件中配置  
     android {     sourceSets {         main {             jniLibs.srcDirs = ['libs']         }     } }
    注意事项运行出错:Didn't find class "com.hyphenate.chat.adapter.EMACallSession",原因是hyphenatechat_3.2.0.jar包内没有该类。解决办法:导入Demo源码中EaseUI库里面的hyphenatechat_3.2.0.jar替换。软件架构MVCMVC应用于Ruby on Rails, Spring Framework, iOS开发和 ASP.NET等。
    •  Model: 获取数据的业务逻辑,网络操作,数据库操作
    •  View: UI
    •  Controller: 操作Model层获取数据传递给UI

    002.png

    服务器端的MVC

    003.png

    Android中MVCAndroid中并没有清晰的MVC框架,如果把Activity当做Controller,根据我们实际开发经验,里面会有大量的UI操作,所以V和C就傻傻分不清了。
    •  Model:Java Bean, NetworkManager, DataBaseHelper
    •  View: xml res
    •  Controller: Activity Fragment
    •  ArrayList-ListView-Adapter(MVC)
    MVPMVP主要应用于ASP.NET等。MVP与MVC主要区别是View和Model不再耦合。
    •  Model: 获取数据的业务逻辑,网络操作,数据库操作
    •  View: UI
    •  Presenter: 操作Model层获取数据传递给UI
     

    004.png

    MVVMMVVM主要应用于WPF, Silverlight, Caliburn, nRoute等。[list=1]
  •  Model: 获取数据的业务逻辑,网络操作,数据库操作
  •  View: UI
  •  ViewModel: 将View和Model绑定

  • 005.png

    Android中MVVM 软件架构的核心思想
    分层分模块

    006.png

     参考 准备好了么? 开车啦!!! 包的创建
    •  adapter 存放适配器
    •  app 存放常量类,Application类以及一些app层级的全局类
    •  database 数据库相关类
    •  event EventBus使用的事件类
    •  factory 工厂类
    •  model 数据模型
    •  presenter MVP模型中的Presenter类
    •  ui 存放activity和fragment
    •  utils 工具类
    •  view MVP模型中的View类
    •  widget 自定义控件
    基类的创建
    •  BaseActivity
    •  BaseFragment
    Splash界面功能需求[list=1]
  • 如果没有登录,延时2s, 跳转到登录界面
  • 如果已经登录,则跳转到主界面
  • MVP实现
    •  SplashView
    •  SplashPresenter
    判断是否登录环信
    @Overridepublic void checkLoginStatus() {    if (EMClient.getInstance().isLoggedInBefore() && EMClient.getInstance().isConnected()) {        mSplashView.onLoggedIn();    } else {        mSplashView.onNotLogin();    }}
     登录界面功能需求[list=1]
  • 点击登录按钮或者点击虚拟键盘上的Action键都能发起登录操作
  • 点击新用户,跳转到注册界面。
  • MVP实现
    •  LoginView
    •  LoginPresenter
    IME Options注意配置EditText的imeOptions属性时,需要配合inputType才能起作用。
    android:imeOptions="actionNext"//下一个android:imeOptions="actionGo"//启动android:imeOptions="actionDone"//完成android:imeOptions="actionPrevious"//上一个android:imeOptions="actionSearch"//搜索android:imeOptions="actionSend"//发送
    监听软键盘Action事件,发起登录 
    private TextView.OnEditorActionListener mOnEditorActionListener = new TextView.OnEditorActionListener() {    @Override    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {        if (actionId == EditorInfo.IME_ACTION_GO) {            startLogin();            return true;        }        return false;    }};
    EMCallBack的适配器     EMCallBack是环信的一个请求回调接口,包括请求成功的回调onSuccess,请求失败的回调onError和请求进度回调onProgress,但在实际使用过程中,通常只使用到请求成功和失败的回调,请求进度回调通常留在那里成了一个空方法,对于一个有代码洁癖的搬砖师来说,这是很难受的。所以我们可以创建一个适配器类实现这个接口,使用时用适配器类来替换EMCallBack接口,这样只需要覆写我们想覆写的方法就可以了。
    //EMCallBack接口的适配器public class EMCallBackAdapter implements EMCallBack{    @Override    public void onSuccess() {    }    @Override    public void onError(int i, String s) {    }    @Override    public void onProgress(int i, String s) {    }}//EMCallBack适配器的使用private EMCallBackAdapter mEMCallBack = new EMCallBackAdapter() {    @Override    public void onSuccess() {        ThreadUtils.runOnUiThread(new Runnable() {            @Override            public void run() {                mLoginView.onLoginSuccess();            }        });    }    @Override    public void onError(int i, String s) {        ThreadUtils.runOnUiThread(new Runnable() {            @Override            public void run() {                mLoginView.onLoginFailed();            }        });    }};
    Android6.0动态权限管理

    009.png

    Andrioid6.0对权限进行了分组,涉及到用户敏感信息的权限只能动态的去获取。当应用的targetSdkVersion小于23时,会默认采用以前的权限管理机制,当targetSdkVersion大于等于23时并且运行在Andorid6.0系统上,它才会采用这套新的权限管理机制。参考 动态获取写磁盘权限环信SDK内部维护了一个数据库来存储聊天记录等数据,需要读写磁盘的权限,所以我们在用户登录之前要申请该权限。类似的,很多同学在集成百度地图或者高德地图时常常不能正常定位,往往是忽略了在Android6.0上需要获取位置的权限。
    /** * 是否有写磁盘权限 */private boolean hasWriteExternalStoragePermission() {    int result = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);    return result == PermissionChecker.PERMISSION_GRANTED;}/** * 申请权限 */private void applyPermission() {    String permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};    ActivityCompat.requestPermissions(this, permissions, REQUEST_WRITE_EXTERNAL_STORAGE);}/** * 申请权限回调 */@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String permissions, @NonNull int grantResults) {    switch (requestCode) {        case REQUEST_WRITE_EXTERNAL_STORAGE:            if (grantResults[0] == PermissionChecker.PERMISSION_GRANTED) {                login();            } else {                toast(getString(R.string.not_get_permission));            }            break;    }}
    注册界面

    010.png


    011.png

    功能需求[list=1]
  • 用户名的长度必须是3-20位,首字母必须为英文字符,其他字符则除了英文外还可以是数字或者下划线。
  • 密码必须是3-20位的数字。
  • 密码和确认密码一致
  • MVP实现
    • RegisterView
    • RegisterPresenter
    正则表达式 
    private static final String USER_NAME_REGEX = "^[a-zA-Z]\\w{2,19}$";//用户名的正则表达式private static final String PASSWORD_REGEX = "^[0-9]{3,20}$";//密码的正则表达式
    •  ^ 匹配输入字符串的开始位置
    • [a-zA-Z]     字符范围。匹配指定范围内的任意字符。
    • \w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
    •  $ 匹配输入字符串的结束位置
     注册流程[list=1]
  •  实际项目中,注册会将用户名和密码注册到APP的服务器,然后APP的服务器再通过REST API方式注册到环信服务器。
  •  由于本项目没有APP服务器,会将用户数据注册到第三方云数据库Bmob,注册成功后,再在客户端发送请求注册到环信服务器。

  • 012.png

    云数据库 Bmob集成Bmob开发文档 
    •  注册创建应用
    •  下载SDK
    •  导入SDK
    •  初始化SDk
    隐藏软键盘在点击注册按钮发起注册流程后,我们需要隐藏掉软键盘并且弹出进度条。
    protected void hideSoftKeyboard() {    if (mInputMethodManager == null) {        mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);    }    mInputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);}
    注册用户到BmobBmob用户管理
    private void registerBmob(final String userName, final String pwd) {    User user = new User(userName, pwd);    user.signUp(new SaveListener() {        @Override        public void done(User user, BmobException e) {            if (e == null) {                registerEaseMob(userName, pwd);            } else {                notifyRegisterFailed(e);            }        }    });}
    注册到环信
    private void registerEaseMob(final String userName, final String pwd) {    ThreadUtils.runOnBackgroundThread(new Runnable() {        @Override        public void run() {            try {                EMClient.getInstance().createAccount(userName, pwd);                ThreadUtils.runOnUiThread(new Runnable() {                    @Override                    public void run() {                        mRegisterView.onRegisterSuccess();                    }                });            } catch (HyphenateException e) {                e.printStackTrace();                ThreadUtils.runOnUiThread(new Runnable() {                    @Override                    public void run() {                        mRegisterView.onRegisterError();                    }                });            }        }    });}
    用户名已注册的处理当我们注册一个用户名到Bmob云数据库后,使用相同的用户名再次注册,Bmob会返回错误码202,信息为username '%s' already taken。所以我们应该处理该错误,通知用户换一个用户名注册。Bmob错误码  
    private void notifyRegisterFailed(BmobException e) {    if (e.getErrorCode() == Constant.ErrorCode.USER_ALREADY_EXIST) {        mRegisterView.onResisterUserExist();    } else {        mRegisterView.onRegisterError();    }}
    主界面

    013.png

    底部导航条 主界面底部是一个Tab导航条,实现的方法可以有很多种,我们可以使用RadioGroup或者FragmentTabHost, 或者我们可以自定义一个控件,为了不重复造轮子,本Demo使用了第三方导航条BottomBar。BottomBar的使用请参考其Github地址。第三方导航条Fragment的切换    
    private OnTabSelectListener mOnTabSelectListener = new OnTabSelectListener() {    @Override    public void onTabSelected(@IdRes int tabId) {        FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();        fragmentTransaction.replace(R.id.fragment_container, FragmentFactory.getInstance().getFragment(tabId)).commit();    }};
    动态界面

    014.png

    MVP实现
    •  DynamicView
    •  DynamicPresenter
    动态界面目前只实现了退出登录功能。
    @Overridepublic void logout() {    mDynamicView.onStartLogout();    EMClient.getInstance().logout(true, mEMCallBackAdapter);}
    联系人界面

    015.png


    016.jpg

    MVP实现
    •  ContactView
    •  ContactPresenter
    使用RecyclerView实现联系人列表RecyclerView类似ListView, 但比ListView更高级更灵活。使用RecyclerView必须指定一个适配器和一个布局管理器,经常有同学忘记设置布局管理器,结果导致RecyclerView什么也没有显示。适配器通常继承自RecyclerView.Adapter。RecyclerView提供这些内置管理器:
    •  LinearLayoutManager 线性布局管理器 (类似ListView效果)
    •  GridLayoutManager 网格布局管理器 (类似GridView效果)
    •  StaggeredGridLayoutManager 分散网格布局管理器(瀑布流效果)
    如果要自定义一个布局管理器,请继承自RecyclerView.LayoutManager。另外,RecyclerView在默认情况下启用增添与删除item的动画,如果要定义这些动画,请继承RecyclerView.ItemAnimator并使用RecyclerView.setItemAnimator()。 初始化    
    private void initRecyclerView() {    mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));//设置布局管理器    mRecyclerView.setHasFixedSize(true);//设置true如果列表内容不会影响RecyclerView的大小    mContactListAdapter = new ContactListAdapter(getContext(), mContactPresenter.getContactList());    mContactListAdapter.setOnItemClickListener(mOnItemClickListener);    mRecyclerView.setAdapter(mContactListAdapter);}
    ContactListAdapter的实现 ContactListItemView

    017.png

    运用模块化的思想,将联系人的列表项抽取成一个独立的自定义组合式控件ContactListItemView。ContactListItemView只需传入一个与之对应的数据模型ContactListItem即可完成渲染。联系人是否在同一个组

    018.png

    当多个联系人的首字符相同时,只有第一个ContactListItemView显示首字符,后续首字符相同的ContactListItemView均不显示首字符。在ContactListItem中声明一个布尔型变量showFirstLetter来标记是否显示首字符。该变量在创建ContactListItem时赋值。
    /** * 获取联系人列表数据 * @throws HyphenateException */private void startGetContactList() throws HyphenateException {    List contacts = EMClient.getInstance().contactManager().getAllContactsFromServer();    DatabaseManager.getInstance().deleteAllContacts();    if (!contacts.isEmpty()) {        for (int i = 0; i < contacts.size(); i++) {            ContactListItem item = new ContactListItem();            item.userName = contacts.get(i);            if (itemInSameGroup(i, item)) {                item.showFirstLetter = false;            }            mContactListItems.add(item);            saveContactToDatabase(item.userName);        }    }}/** * 当前联系人跟上个联系人比较,如果首字符相同则返回true * @param i 当前联系人下标 * @param item 当前联系人数据模型 * @return true 表示当前联系人和上一联系人在同一组 */private boolean itemInSameGroup(int i, ContactListItem item) {    return i > 0 && (item.getFirstLetter() == mContactListItems.get(i - 1).getFirstLetter());}
    CardView的使用

    019.png

    CardView继承自FrameLayout, 是Material Design里面的卡片设计,带有圆角和阴影效果。CardView效果非常好看,但也不能滥用,比如:

    020.png

    更多的使用规范,请参考Material Design的设计规范Material Design Cards。这里我们给ContactListItemView的布局里面包了一层CardView,实际上是不可取的做法,这里只是为了展示CardView的使用姿势,实际项目中请大家遵循Material Design的设计规范。参考SwipeRefreshLayout的使用  
    //设置SwipeRefreshLayout的颜色mSwipeRefreshLayout.setColorSchemeResources(R.color.qq_blue, R.color.qq_red);//设置SwipeRefreshLayout的监听器mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
    自定义控件SlideBar分类字符数组 
    rivate static final String SECTIONS = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"        , "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
     绘制居中文本FontMetrics

    021.png

    @Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {    mTextSize = h * 1.0f / SECTIONS.length;//计算分配给字符的高度    Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();    float mTextHeight = fontMetrics.descent - fontMetrics.ascent;//获取绘制字符的实际高度    mTextBaseline = mTextSize / 2 + mTextHeight/2 - fontMetrics.descent;//计算字符居中时的baseline}@Overrideprotected void onDraw(Canvas canvas) {    float x = getWidth() * 1.0f / 2;    float baseline = mTextBaseline;    for(int i = 0; i < SECTIONS.length; i++) {        canvas.drawText(SECTIONS[i], x, baseline, mPaint);        baseline += mTextSize;    }}[/i]
    在ContactFragment里面监听SlideBar的事件 当用户在SlideBar上ACTION_DOWN和ACTION_MOVE时,如果用户触摸到的首字符发生变化,则会回调监听器onSectionChange,当ACTION_UP时回调onSlidingFinish()。
    private SlideBar.OnSlideBarChangeListener mOnSlideBarChangeListener = new SlideBar.OnSlideBarChangeListener() {    @Override    public void onSectionChange(int index, String section) {        mSection.setVisibility(View.VISIBLE);//显示中间绿色背景的首字符        mSection.setText(section);        scrollToSection(section);//滚动RecyclerView到用户滑到的首字符    }    @Override    public void onSlidingFinish() {        mSection.setVisibility(View.GONE);//隐藏中间绿色背景的首字符    }};
    联系人点击事件当单击联系人时跳转到聊天界面,当长按联系人时弹出Dialog询问用户是否删除好友。  
    private ContactListAdapter.OnItemClickListener mOnItemClickListener = new ContactListAdapter.OnItemClickListener() {    /**     * 单击跳转到聊天界面     * @param name 点击item的联系人名字     */    @Override    public void onItemClick(String name) {        startActivity(ChatActivity.class, Constant.Extra.USER_NAME, name);    }    /**     * 长按删除好友     * @param name 点击item的联系人名字     */    @Override    public void onItemLongClick(final String name) {        showDeleteFriendDialog(name);    }};
    添加好友界面 

    022.png

     MVP实现  
    • AddFriendView
      • AddFriendPresenter
    搜索用户 
    @Overridepublic void searchFriend(final String keyword) {    mAddFriendView.onStartSearch();    //注:模糊查询只对付费用户开放,付费后可直接使用。    BmobQuery query = new BmobQuery();    query.addWhereContains("username", keyword).addWhereNotEqualTo("username", EMClient.getInstance().getCurrentUser());    query.findObjects(new FindListener() {        @Override        public void done(List list, BmobException e) {            processResult(list, e);        }    });}
     greenDAOgreenDAO是Android SQLite数据库ORM框架的一种。ORM即对象关系映射, object/relational mapping, 将Java对象映射成数据库的表。我们需要存储联系人数据到数据库,在添加好友界面,需要判断搜索出来的用户是否已经是联系人,如果已经是好友,则应显示“已添加”。其他ORM框架 参考 创建实体类    
    @Entitypublic class Contact {    @Id    public Long id;    public String userName;}
     初始化    
    public void init(Context context) {    DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(context, Constant.Database.DATABASE_NAME, null);    SQLiteDatabase writableDatabase = devOpenHelper.getWritableDatabase();    DaoMaster daoMaster = new DaoMaster(writableDatabase);    mDaoSession = daoMaster.newSession();}
    保存联系人
    public void saveContact(String userName) {    Contact contact = new Contact();    contact.setUsername(userName);    mDaoSession.getContactDao().save(contact);}
    查询联系人    
    public List queryAllContacts() {    List list = mDaoSession.getContactDao().queryBuilder().list();    ArrayList contacts = new ArrayList();    for (int i = 0; i < list.size(); i++) {        String contact = list.get(i).getUsername();        contacts.add(contact);    }    return contacts;}
     删除所有联系人
    public void deleteAllContacts() {    ContactDao contactDao = mDaoSession.getContactDao();    contactDao.deleteAll();}
    发送好友请求 AddFriendItemView里面处理添加好友的点击事件    
    @OnClick(R.id.add)public void onClick() {    String friendName = mUserName.getText().toString().trim();    String addFriendReason = getContext().getString(R.string.add_friend_reason);    AddFriendEvent event = new AddFriendEvent(friendName, addFriendReason);//通过EventBus发布添加好友事件    EventBus.getDefault().post(event);}
    AddFriendPresenterImpl实现发送好友请求   
    @Subscribe(threadMode = ThreadMode.BACKGROUND)public void addFriend(AddFriendEvent event) {    try {        EMClient.getInstance().contactManager().addContact(event.getFriendName(), event.getReason());        ThreadUtils.runOnUiThread(new Runnable() {            @Override            public void run() {                mAddFriendView.onAddFriendSuccess();            }        });    } catch (HyphenateException e) {        e.printStackTrace();        ThreadUtils.runOnUiThread(new Runnable() {            @Override            public void run() {                mAddFriendView.onAddFriendFailed();            }        });    }
    在联系人列表中监听联系人变化当新增好友或者被好友删除时刷新联系人列表。 
    private EMContactListenerAdapter mEMContactListener = new EMContactListenerAdapter() {    @Override    public void onContactAdded(String s) {        mContactPresenter.refreshContactList();    }    @Override    public void onContactDeleted(String s) {        mContactPresenter.refreshContactList();    }};
    聊天界面 

    023.png

    MVP实现 
    •  ChatView
      •  ChatPresenter
    UI监听发送按钮的状态变化     
    mEdit.addTextChangedListener(mTextWatcher);private TextWatcherAdapter mTextWatcher = new TextWatcherAdapter() {    @Override    public void afterTextChanged(Editable s) {        mSend.setEnabled(s.length() != 0);    }};
    动画文件  
    •  anim文件夹:存放补间动画
      •  animator文件夹:存放属性动画
        •  drawable文件夹:存放帧动画
    发送消息的进度动画    
                                    
    9文件制作官方说明

    24.png


    25.png

    发送一条消息 返回消息的类型 根据是发送的消息还是接受的消息,来创建不同的item。
    @Overridepublic int getItemViewType(int position) {    EMMessage message = mMessages.get(position);    return message.direct() == EMMessage.Direct.SEND ? ITEM_TYPE_SEND_MESSAGE : ITEM_TYPE_RECEIVE_MESSAGE;}
    是否显示时间戳的判断 
    /** * 如果两个消息之间的时间太近,就不显示时间戳 */private boolean shouldShowTimeStamp(int position) {    long currentItemTimestamp = mMessages.get(position).getMsgTime();    long preItemTimestamp = mMessages.get(position - 1).getMsgTime();    boolean closeEnough = DateUtils.isCloseEnough(currentItemTimestamp, preItemTimestamp);    return !closeEnough;}
    更新消息的状态处理三种消息状态,正在发送中INPROGRESS,发送成功SUCCESS,发送失败FAIL。  
    private void updateSendingStatus(EMMessage emMessage) {    switch (emMessage.status()) {        case INPROGRESS:            mSendMessageProgress.setVisibility(VISIBLE);            mSendMessageProgress.setImageResource(R.drawable.send_message_progress);            AnimationDrawable drawable = (AnimationDrawable) mSendMessageProgress.getDrawable();            drawable.start();            break;        case SUCCESS:            mSendMessageProgress.setVisibility(GONE);            break;        case FAIL:            mSendMessageProgress.setImageResource(R.mipmap.msg_error);            break;    }}
     接收一条消息 监听消息的接收,当收到一条消息后,通知MessageListAdapter进行刷新,并且滚动RecyclerView到底部。
    private EMMessageListenerAdapter mEMMessageListener = new EMMessageListenerAdapter() {    @Override    public void onMessageReceived(final List list) {        ThreadUtils.runOnUiThread(new Runnable() {            @Override            public void run() {                final EMMessage emMessage = list.get(0);                mChatPresenter.makeMessageRead(mUserName);                mMessageListAdapter.addNewMessage(emMessage);                smoothScrollToBottom();            }        });    }};
    初始化聊天记录    
    @Overridepublic void loadMessages(final String userName) {    ThreadUtils.runOnBackgroundThread(new Runnable() {        @Override        public void run() {            EMConversation conversation = EMClient.getInstance().chatManager().getConversation(userName);            if (conversation != null) {                //获取此会话的所有消息                List messages = conversation.getAllMessages();                mEMMessageList.addAll(messages);                //指定会话消息未读数清零                conversation.markAllMessagesAsRead();            }            ThreadUtils.runOnUiThread(new Runnable() {                @Override                public void run() {                    mChatView.onMessagesLoaded();                }            });        }    });}
    加载更多聊天记录   
    @Overridepublic void loadMoreMessages(final String userName) {    if (hasMoreData) {        ThreadUtils.runOnBackgroundThread(new Runnable() {            @Override            public void run() {                EMConversation conversation = EMClient.getInstance().chatManager().getConversation(userName);                EMMessage firstMessage = mEMMessageList.get(0);                //SDK初始化加载的聊天记录为20条,到顶时需要去DB里获取更多                //获取startMsgId之前的pagesize条消息,此方法获取的messages SDK会自动存入到此会话中,APP中无需再次把获取到的messages添加到会话中                final List messages = conversation.loadMoreMsgFromDB(firstMessage.getMsgId(), DEFAULT_PAGE_SIZE);                hasMoreData = (messages.size() == DEFAULT_PAGE_SIZE);                mEMMessageList.addAll(0, messages);                ThreadUtils.runOnUiThread(new Runnable() {                    @Override                    public void run() {                        mChatView.onMoreMessagesLoaded(messages.size());                    }                });            }        });    } else {        mChatView.onNoMoreData();    }}
    会话界面

    26.png

     MVP实现  
    •  ConversationView
      •  ConversationPresenter



    加载所有会话
    @Override
    public void loadAllConversations() {
    ThreadUtils.runOnBackgroundThread(new Runnable() {
    @Override
    public void run() {
    Map conversations = EMClient.getInstance().chatManager().getAllConversations();
    mEMConversations.addAll(conversations.values());
    //根据最后一条消息的时间进行排序
    Collections.sort(mEMConversations, new Comparator() {
    @Override
    public int compare(EMConversation o1, EMConversation o2) {
    return (int) (o2.getLastMessage().getMsgTime() - o1.getLastMessage().getMsgTime());
    }
    });
    ThreadUtils.runOnUiThread(new Runnable() {
    @Override
    public void run() {
    mConversationView.onAllConversationsLoaded();
    }
    });
    }
    });
    }

    未读消息计数更新 
    环信官方文档
    会话列表中未读消息的更新 
      
    private EMMessageListenerAdapter mEMMessageListenerAdapter = new EMMessageListenerAdapter() {

    @Override
    public void onMessageReceived(List list) {
    ThreadUtils.runOnUiThread(new Runnable() {
    @Override
    public void run() {
    toast(getString(R.string.receive_new_message));
    //当收到一个新消息时,重新加载会话列表。
    mConversationPresenter.loadAllConversations();
    }
    });
    }
    };

    BottomBar badge的更新 
      
    private EMMessageListenerAdapter mEMMessageListenerAdapter = new EMMessageListenerAdapter() {

    //该回调在子线程中调用
    @Override
    public void onMessageReceived(List list) {
    updateUnreadCount();
    }
    };

    private void updateUnreadCount() {
    ThreadUtils.runOnUiThread(new Runnable() {
    @Override
    public void run() {
    BottomBarTab bottomBar = mBottomBar.getTabWithId(R.id.conversations);
    int count = EMClient.getInstance().chatManager().getUnreadMsgsCount();
    bottomBar.setBadgeCount(count);
    }
    });
    }

    标记消息已读 
    加载聊天数据时标记已读 
    //指定会话消息未读数清零
    conversation.markAllMessagesAsRead();

    在聊天界面收到新消息时将消息标记已读
       
    private EMMessageListenerAdapter mEMMessageListener = new EMMessageListenerAdapter() {
    @Override
    public void onMessageReceived(final List list) {
    ThreadUtils.runOnUiThread(new Runnable() {
    @Override
    public void run() {
    final EMMessage emMessage = list.get(0);
    if (emMessage.getUserName().equals(mUserName)) {
    //标记消息已读
    mChatPresenter.makeMessageRead(mUserName);
    mMessageListAdapter.addNewMessage(emMessage);
    smoothScrollToBottom();
    }
    }
    });
    }
    };
     聊天界面返回时更新未读badge 
        
    @Override
    protected void onResume() {
    super.onResume();
    updateUnreadCount();
    }

    消息通知
    通知 
    判断app是否在前台 

     
    public boolean isForeground() {
    ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    List runningAppProcesses = am.getRunningAppProcesses();
    if (runningAppProcesses == null) {
    return false;
    }
    for (ActivityManager.RunningAppProcessInfo info :runningAppProcesses) {
    if (info.processName.equals(getPackageName()) && info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
    return true;
    }
    }
    return false;
    }

     如果app在后台则弹出Notification 
    private void showNotification(EMMessage emMessage) {
    String contentText = "";
    if (emMessage.getBody() instanceof EMTextMessageBody) {
    contentText = ((EMTextMessageBody) emMessage.getBody()).getMessage();
    }

    Intent chat = new Intent(this, ChatActivity.class);
    chat.putExtra(Constant.Extra.USER_NAME, emMessage.getUserName());
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, chat, PendingIntent.FLAG_UPDATE_CURRENT);

    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    Notification notification = new Notification.Builder(this)
    .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.avatar1))
    .setSmallIcon(R.mipmap.ic_contact_selected_2)
    .setContentTitle(getString(R.string.receive_new_message))
    .setContentText(contentText)
    .setPriority(Notification.PRIORITY_MAX)
    .setContentIntent(pendingIntent)
    .setAutoCancel(true)
    .build();
    notificationManager.notify(1, notification);
    }
     声音  

     初始化SoundPool
    private void initSoundPool() {
    mSoundPool = new SoundPool(2, AudioManager.STREAM_MUSIC, 0);
    mDuanSound = mSoundPool.load(this, R.raw.duan, 1);
    mYuluSound = mSoundPool.load(this, R.raw.yulu, 1);
    }

     播放音效 
      
    private EMMessageListenerAdapter mEMMessageListenerAdapter = new EMMessageListenerAdapter() {

    @Override
    public void onMessageReceived(List list) {
    if (isForeground()) {
    mSoundPool.play(mDuanSound, 1, 1, 0, 0, 1);
    } else {
    mSoundPool.play(mYuluSound, 1, 1, 0, 0, 1);
    showNotification(list.get(0));
    }
    }
    };
     多设备登录 
    环信官方文档
    private EMConnectionListener mEMConnectionListener = new EMConnectionListener() {
    @Override
    public void onConnected() {

    }

    @Override
    public void onDisconnected(int i) {
    if (i == EMError.USER_LOGIN_ANOTHER_DEVICE) {
    ThreadUtils.runOnUiThread(new Runnable() {
    @Override
    public void run() {
    startActivity(LoginActivity.class);
    toast(getString(R.string.user_login_another_device));
    }
    });
    }
    }
    };

    github地址:https://github.com/uncleleonfan/QQDemo 收起阅读 »

    直聘学院| 独角兽产品养成记【深度对话摩拜单车/喜马拉雅FM等产品总监】

    如何让一款产品快速风靡全国? 产品工作如何让你们的市场、运营、技术同事为你欢呼尖叫? 怎么样的产品策略能不靠投资后来居上逆袭行业排名第一? 2016年11月20日 让那些独角兽产品的创造者们和我们一起聊聊: 独角兽产品养成记! 活动嘉宾...
    继续阅读 »
    如何让一款产品快速风靡全国?
    产品工作如何让你们的市场、运营、技术同事为你欢呼尖叫?
    怎么样的产品策略能不靠投资后来居上逆袭行业排名第一?

    2016年11月20日
    让那些独角兽产品的创造者们和我们一起聊聊:

    独角兽产品养成记!

    活动嘉宾介绍:


    1.jpg



    2.jpg



    3.jpg


    活动流程

    13:30-14:00丨现场签到 & 交流
    14:00-15:00丨摩拜单车Stella主题分享《线上线下结合的产品实践》
    15:00-16:00丨涂图石强主题分享《如何用产品思维驱动业务实践》
    16:00-17:00丨Neta干田主题分享《运营背后的坑是这么填的》
    17:00 ——  丨全场合影
     
    关于直聘学院


    直聘学院.jpg


    对话产品总监是BOSS直聘 「 直聘学院 」 旗下系列活动之一。联合明星公司&合作伙伴共同发起,为互联网领域的产品经理从业者举办的专场沙龙,旨在通过纯业内的干货分享,推动互联网行业从业者职业路上进步。


    特别鸣谢


    特别鸣谢.jpg



    合作伙伴.jpg



    战略合作.jpg


    抢票链接:http://gz.huodongxing.com/event/5358825198900
      收起阅读 »