我认为太极有以下几个关键的有价值的特性:
我认为太极在以下领域会有很好的应用:
对于一名仍在不断学习的中老年人而言,我不希望仅仅是学会一两个重复的工具,而是能够更高效的去实践、探索未知的世界。与其学习tensorflow这种集成式的深度学习框架,不如用太极自己实现一个,这样我们才能更深刻的理解其中的过程。我们还可以用太极将数据可视化的展示出来。
学习API是非常无趣的,但对于每一个学习编程的孩子来说,却是一道很高的门槛。很多人都无法实现在屏幕上绘制一个像素,这使我们哪怕拥有了足够的知识,依然缺乏表现力。有了太极,就仿佛打开了一道科研的大门。在科研过程中,我们需要的是快速实验、快速试错。太极就是为此而生的。
]]>游戏引擎,应该允许游戏开发者,轻松的定义实体,操控这些实体的位置、模型、滤镜,最终通过渲染引擎渲染到屏幕上。甚至要提供一些易用的图形化制作工具,比如关卡编辑器,地图编辑器,实体编辑器,脚本语言,配置表,这些输出成数据文件。这些数据将被加载到内存,通过渲染引擎输出到屏幕。
[制作工具]–>静态数据–>[用户输入处理、AI update、物理引擎]–>动态数据–>[渲染引擎]–>最终画面。
游戏引擎=制作工具+程序API+渲染引擎。
而游戏引擎的关键,就在于处理数据。
哪怕一个最简单的终端贪食蛇游戏(这是我11岁的儿子写的)也需要符合这个标准。这个终端游戏制作工具不需要,他的素材是一些终端字符,google担任了这个制作工具的角色。程序API更新贪食蛇的动态数据,然后渲染引擎将动态数据映射成特定的终端字符打印在屏幕上。
开发一个3D游戏,数据结构更加复杂一些。理清了这个结构之后,3D游戏可能比2D游戏更容易开发一些,因为3D数据结构毕竟更接近真实世界。我们将整个待渲染的世界,称为【场景】,场景中摆放着各种【模型】。对于物理引擎部分,只需要模型的数据就可以了,而对于渲染引擎,则还需要【光源】和【摄像机】。
一个粒子由*位置position、速度velocity、质量mass、力force(加速度acceleration)、阻尼damping(简化一个物体在一个环境中的摩擦力如0.999)等属性,还需要考虑地心引力gravity的影响。
用反质量invertMass模拟无限质量?
位置更新公式:$$ p’=p+dot p t + 1/2 ddot p t^2 $$ 其中,$dot p$表示速度,$ddot p$表示加速度。在30fps时,t=0.033 后一项可以忽略。简化为 $$ p’=p+dot p t$$
速度更新公式: $$ dot p’ = dot p d^t + ddot p t $$ d为阻尼,
以上模拟对于慢速物体没有问题,但对于子弹、炮弹这种高速物体则不适用,因为可能在一帧之内子弹已穿过物体,无法与目标发生碰撞。因此对于这种情况,我们则需要将子弹想象为一个激光(或抛物线)。对于目标所受到的冲击,则需要通过动量守恒、能量守恒公式进行计算(TODO)。
重力是恒定的,但还有很多其他的力是动态生成的,爆炸、发射、风。因此我们要创造一个『力生成器』在每一帧执行它的updateForce方法,来设定物体受到的外力。
弹簧是一种普遍的模型,那些柔软的材质都可以抽象为大量的弹簧。根据胡克定理$$f=-k Delta l$$,我们可以写一个弹簧力生成器。
除了弹簧系统外,还有一类硬约束是紧密连接的对象,比如铁链、四肢。他们的关键在于连接点的速度是一致的。
然后我们要将所有的东西整合到一起成为一个物理引擎,在每一帧updatePhysics()
我们要将物理引擎从点升级到体,刚体的碰撞盒与他的质点。刚体的旋转。
碰撞检测(TODO)
参考:
《Game Physics Engine Development》2nd Edition by Ian Millington
]]>物理量 | 符号 | 公式 | 国际单位制 | 单位符号 | 注释 |
---|---|---|---|---|---|
辐射能(Radiant energy) | $Q_e$ | 焦耳 | $J$ | 能量。 | |
辐射通量(Radiant flux) | $Phi_e$ | $Phi=(dQ)/(dt)$ | 瓦特 | $W$ | 每单位时间的辐射能量,亦作“辐射功率”。 |
辐射强度(Radiant intensity) | $I_e$ | $I=(dPhi)/(d omega) | 瓦特每球面度 | $W*sr^(-1)$ | 每单位立体角的辐射通量。 |
辐照度(Irradiance)(辉度) | $E_e$ | $E=(dPhi)/(dA)=int_(Omega) L(omega)cos theta d omega$ | 瓦特每平方米 | $W*m^(-2)$ | 入射表面的辐射通量 |
辐射率(Radiance)(光亮度) | $L_e$ | $(d^2Phi)/(dAcos theta d omega)$ | 瓦特每球面度每平方米 | $W*sr^(-1)*m^(-2)$ | 每单位立体角每单位投射表面的辐射通量。相当于辐射强度在dA上的微分 |
BRDF由Fred Nicodemus在1965年提出,函数如下:
$$f_r(omega_i,omega_r) = (dL_r(omega_r))/(dE_i(omega_i)) = (dL_r(omega_r))/(L_i(omega_i)cos theta_i d omega_i) $$
这个公式之所以定义为辐射率(radiance)和辐照度(irradiance)之比,而不是radiance和radiance之比,或irradiance和irradiance之比。是因为当考虑入射时,我们需要考虑入射光在面积上的分量,所以irradiance译为辐照度。当考虑反射时,我们需要考虑每立体角的辐射通量,并且这个辐射通量最终投影在屏幕(视网膜)面积上的辐射通量,因此我们用辐射率。如果我们用点光源,入射光的计算似乎也是可以用辐射率的,但有时我们还要考虑平行光的情况,那么对于入射光就不存在每立体角的概念了,因此对于入射光照我们用辐照度,反射我们用辐射率。
是一些半透明物质比如皮肤、玉石、大理石、塑料等。当光入射到材料表面后,一部分被反射、一部分被吸收、还有一部分经历透射,透射光在材料内部进行多次不规则的反射之后,又从不同角度反射了回来。
当光从一种折射率为$n_1$的介质向另一种折射率为$n_2$的介质传播时,在两者的交界处可能会同时发生光的反射和折射。菲涅尔方程描述了光波的不同分量被折射和反射的情况,也描述了波反射时的相变。光线会随着我们的观察角度而反射不同的亮度,当我们以垂直与水面的角度观察池塘时,我们可以看到池塘的底部,但当我们以平行于水面的角度观察水面时,反射光则会很强我们无法看到池底。
微表面理论假设材质的表面是由不同方向的微小细节平面(microfacet)所构成,反射光线由这些微表面的法线分布决定。我们用法线分布函数(Normal Distribution Function,NDF),D(h) 来描述表面的法线分布概率。h表示视角与入射光角度之间的半程向量。
$$f(i,o) = (F(i,h)G(i,o,h)D(h))/(4(n,i)(n,o))$$
其中F(i,h)表示菲涅尔项,表示所有反射的比例。G(i,o,h) 表示自投影项,当光线几乎平射于微表面时,光线则将被粗糙的表面自我遮挡掉。D(h)表示法线分布。
参考:
Wikipedia:《Bidirectional reflectance distribution function》
《Real-Time Rendering, 4th edition》
]]>吐槽结束,进入正题。谈谈我此刻对线性代数的理解,探讨一下他的本质到底是什么。我们是否会问自己,加减乘除的本质到底是什么?我们之所以不这么问,是因为我们已经理解了四则运算的本质。我们不会问关于十进制的本质,因为日常生活中已经给我们建立了足够多的经验。但我们在学龄前的阶段,我们则可能对十进制和加减乘除充满了困惑。但我们接触线性代数太晚,我们并没有足够的练习和日常应用使我们建立起感性认识。当我们在商店里消费的时候四则运算不断强化着我们的认知,但线性代数缺少这样的机会。当我们被教授四则运算时,老师把我们当作一个普通的人类,会告诉我们3个苹果+2个苹果=5个苹果,这种现实世界的例子帮助我们更好的理解了四则运算。但当我们学习线性代数时,我们则变成了一个个抽象的理性机器,这个系统只告诉我们各种定义、运算规则,然后要求我们像计算机一样的运行,计算出结果。What are we doing?我们怎么可能不懵圈呢?线性代数就是一个增强版的加减乘除,但没有足够的案例使我们不知道我们的计算究竟代表着什么?AlphaGo就算赢了李世石,但他不知道自己在干什么。我们作为人类的尊严在哪里?我又没控制好自己的情绪,让我们回到正题。《线性代数及其应用》是一本很好的教材,他和国内教材最大的区别就在于“应用”上,这本书中列举了大量的例子来说明线性代数的应用。这本书的开头说道“线性代数是一门语言,必须用学习外语的方法每天学习这种语言”。
我们从鸡兔同笼来举个例子。鸡兔同笼是小学阶段的奥数题,也就是在小学的数学语言中,这是一道很难描述的题。到了中学阶段我们可以用未知数x表示鸡的数量,未知数y表示兔的数量,并列出方程。而对于线性代数的语言,我们用向量$$((a),(b))$$表示鸡和兔的数量,如果我们有非常多的未知数,我们不希望定义太多的未知数符号,我们直接用$$x$$表示这个n维变量。我们有一个变换矩阵$$[[1,1],[2,4]]$$ 表示鸡有1个头2只脚,兔有1个头4只脚 。如果有3只鸡5只兔则 $$[[1,1],[2,4]]*[[3],[5]]=[[3*1+5*1],[3*2+5*4]]=[[8],[26]]$$,它代表着我们将一个“鸡兔向量”映射到了“头脚向量”的空间中,共有8只头,26只脚。
我们知道函数是一种映射,$$f(x)=y$$代表将$x$到$y$的映射关系。矩阵乘法叫做线性变换,线性变换是一种函数映射,但函数映射不一定是线性变化。因此线性变换是符合函数的性质的。如果函数是可逆的,则有$$x=f^(-1)(y)$$,同样的,对于矩阵而言,$$若A是可逆的,且Ax=b,则x=A^(-1)b。设A=[[a,b],[c,d]],则A^(-1)=1/(ad-bc)[[d,-b],[-c,a]]$$。
对于鸡兔同笼问题,$$A=[[1,1],[2,4]],则A^(-1)=1/2[[4,-1],[-2,1]]$$。$$当有8头26脚时,x=1/2[[4,-1],[-2,1]]*[[8],[26]]=1/2[[4*8-26],[-2*8+26]]=1/2[[6],[10]]=[[3],[5]]$$,即3只鸡5只兔。最重要的是,这整个计算过程,计算机可以轻松的完成,并且可以用定义标准化的操作,因为操作标准化,计算机可以被设计的更加擅长处理这类操作。这就是线性代数得到广泛应用的一个最重要原因。
线性变换可能进行多次,就像映射可以进行多次一样。因为矩阵的乘法就是一种特殊的函数,函数满足结合律$$g(f(h(x)))=((g @ f)(h(x)))$$,所以矩阵乘法也符合结合律$$A(BC)=(AB)C$$。多次映射之后是一个新的映射,多次变换之后也是一个新的变换,所以我们可以将这些变换矩阵预先乘好,以增加每次计算的效率。也可以将一个复杂变换拆解为多个简单变换,使我们能更好的理解其性质。
我们可以从鸡兔变换到头脚,我们也可以从产量变换到成本收益(这是经济学的应用),我们也可以从速度变换到阻力(这是空气动力学的应用),我们也可以将3D空间变换到3D或2D空间(这是计算机图形学的应用),我们也可以将用户行为维度变换到兴趣标签维度(这是机器学习推荐系统的应用)。这都说明了线性代数是一门“语言”,是一个工具。并不是因为线性代数,所以这些定理存在,而是因为这些规律本身存在,才能有线性代数这门工具。人类是巧妙的“发明”了线性代数,而不是“发现”了线性代数。线性代数这门语言可以使我们避免基本代数语言的变量名爆炸。人类日常语言中有你我他这那等代词,中文有甲乙丙丁这种天干地支可以用作代词,英文则有a,b,cd可以使用。日常的代词使用是随意的,很多时候是不严谨的。代数学的代词在使用前则需要对它进行准确的定义。而线性代数则将最常使用的一些操作提取了出来,使我们免于重复的定义大量性质类似的代词。在线性代数中,鸡兔数量,头脚数量与空间中点的xyz轴位置都是同一种性质的。研究飞机表面的气流的过程包含了反复求解大型的线性方程组$$Ax=b$$,涉及的变量个数达到2百万个。可以说线性代数的发展完全是随着其应用发展的,如果有一本按照历史发展顺序描述线性代数的书,一定可以达到很好的教学效果。
行列式的出现是远早于矩阵的。Determinant是决定的意思,(下文称之为决定值,“行列式”翻译的无味,既没有描述其决定性质,也没有说明其结果是特定的值):决定值是否不为0决定了一个线性方程组是否有唯一解。所谓线性方程组,就是一次方程组。这个结论最早见于《九章算术》(有一种观点认为很多思想是在明朝由中国传至欧洲,而清朝恰好是中国的一个倒退,而欧洲则顺势伪造了一套其文明独立发展的历史叙事,这里不展开描述了)。决定值的这个性质在欧洲由莱布尼茨最早提出(莱布尼茨在中学西传中扮演着重要角色)。高斯首先使用了determinant这个词,他在数论理论中大量用到了决定值。后来这个词就更多的指一个特殊的函数,即某个表达式,因此中文将其翻译为行列式。
观察我们在鸡兔同笼中得出的结论:$$设A=[[a,b],[c,d]],则A^(-1)=1/(ad-bc)[[d,-b],[-c,a]]$$。因此矩阵A当且仅当$$ad-bc!=0$$时存在逆矩阵,我们记为$$det(A)=|A|=|[a,b],[c,d]|=ad-bc$$。我们继续推广研究$3xx3$矩阵,可以得到:
$$|[a,b,c],[d,e,f],[h,i,j]|=a|[e,f],[h,i]|-b|[d,f],[g,i]|+c|[d,e],[g,h]|=aei+bfg+cdh-ceg-bdi-afh=|[a,d,h],[b,e,i],[c,f,j]|$$
对$4xx4$矩阵则有:
$$|[a,b,c,d],[e,f,g,h],[i,j,k,l],[m,n,o,p]|=a|[f,g,h],[j,k,l],[n,o,p]|-b|[e,g,h],[i,k,l],[m,o,p]|+c|[e,f,h],[i,j,l],[m,n,p]|-d|[e,f,g],[i,j,k],[m,n,o]|$$
依此类推,决定值的公式是一个递归。决定值的几何意义代表代表$1xx1$矩阵变换后的平行四边形的面积,或者说是矩阵变换后面积的放大倍数,即变换矩阵围成的四边形的面积。简单的证明如下图:$S=(a+c)(b+d)-ab-cd-2bc=ad-bc$。推广到3维则表示变换矩阵的围成的立方体的体积。
如果determinant=0,则说明变换之后由面变为了线,或由体变为了面。而线或面无法再变换回面或体。我们同样以鸡兔同笼问题来举例,我们把题目中的兔换成鸭,则变换矩阵为$$A=[[1,1],[2,2]], det(A)=0$$。这个方程组是没有唯一解的,因为无论鸡鸭的比例如何,头脚的比例都是$1:2$。对于这样的矩阵,我们称他的秩为1,秩代表矩阵的维数。一个二维矩阵,可能秩为1,一个三维矩阵可能秩为2也就是一个面,也可能秩为1也就是一条线,甚至秩为0。
TODO 叉乘表示面积和垂直与平面的向量,特征值与特征向量,表示空间中在线性变换中保持稳定的轴,最小二乘法
]]>$a^2+b^2=c^2$。下图是勾股定理的一个直观证明。
三条边可以确定一个三角形,已知三角形的三条边长,如何求出其角度呢?
由$cos,sin$定义可知
$$ c = a * cos beta + b * cos alpha $$
两边同乘c得:
$$ c^2 = ac * cos beta + bc * cos alpha $$
同理可得:
$$ a^2 = ac cos beta + ab * cos gamma $$
$$ b^2 = bc cos alpha + ab cos gamma $$
故:$$ a^2+b^2-c^2 = 2abcosgamma $$
可得:$$ c^2 = a^2 + b^2 - 2ab cos gamma $$
$$令 vec c = vec a - vec b$$, $$theta$$为$$vec a$$ $$vec b$$ 的夹角。余弦定理可以用向量形式写成 $$ | vec c |^2 = |vec a|^2 + |vec b|^2 - 2 |vec a| |vec b| cos theta $$
两个向量的点积是一个标量。向量$$vec a=[a_1, a_2, … a_n]$$与向量$$vec b=[b_1, b_2, … b_n]$$的点积定义为: $$ vec a * vec b = sum_(i=1)^n a_i b_i = a_1 b_1 + a_2 b_2 + … a_n b_n $$。
点积有以下性质(证略):
点积的代数定义简单实用,易于表示,也易于使用计算机程序处理。是线性代数的基本操作之一。
对于任何一个n维向量有 $|vec a|^2=a_1^2+a_2^2+…+a_n^2$。根据勾股定理,这是很显然的。换个角度说如果没有勾股定理,这一步就不存在,后面的内容也不存在了。而勾股定理不是由代数方法证明的,而是独立于代数系统之外的空间基本性质。而空间和时间是宇宙最根本的本质。这就是勾股定理最神奇的地方。
我们根据点积的定义可知:$$ vec a * vec a = a_1 * a_1 + a_2 * a_2 + … a_n * a_n = |vec a|^2$$ 即 $$ vec a * vec a == |vec a|^2$$
我们根据余弦定理的向量表示可得:$$ vec c * vec c = vec a * vec a + vec b * vec b - 2 |vec a| |vec b| cos theta . (1)$$
根据向量的定义 $$ vec c = vec a - vec b $$ 有 $$ vec c * vec c = (vec a - vec b) * (vec a - vec b) = vec a * vec a + vec b * vec b - 2 vec a * vec b . (2)$$
结合等式$$(1)$$、$$(2)$$有 $$vec a * vec b = |vec a| |vec b| cos theta$$。一个看似简单的代数点积操作,竟然和夹角余弦相关,真是不可思议。
点积的几何意义是什么呢?关键就在这个$cos theta$,如果$$|vec b|$$为1时候,我们可以将$$vec a * vec b$$视为$$vec a $$在$$vec b$$方向上的投影长度。
点积的物理意义就是向量在某方向上的投影长度。这在物理上可以表达力在某方向上的投影,光在某方向的投影,速度、加速度在某方向的投影。而点积的操作,可以使我们只需要关心这些物理量的向量表示,而不需要去关心夹角,不需要去计算三角函数。而在统计学、机器学习等方面,余弦可以表示两个向量之间的相似性,比如两个词向量,两个用户的兴趣向量等,应用非常广泛。下面就以计算机图形学举例来说明点积的应用。
冯氏光照模型将一个物体的光照分解为环境光+漫反射光+镜面反射光。
环境光比较简单就是一个常量。而漫反射光,则为光照强度在平面的法线方向的投影,与法线方向一致则光照最强。镜面反射光则为反射光方向在视角方向上的投影,与视角完全一致,则反射光最强。
OpenGL的shader大致如下:
1 |
|
异象出现的那个夜晚,出生了百万个孩子。这是最近百年来出生人口最多的一天。全球实现城市化已经有一百年了,一百年来人类预期寿命不断增加,人口出生率则不断下降。(此处可展开描写,兼叙历史)
异象五年,一首儿歌在孩子们间流传:『XXX,XXX,XXXXXXX,XXX,XXX,XXXXXXX』言辞似无意义,其中『立约』又似有所指。孩童嬉戏玩闹,皆歌之。孩子们天真无邪,他们似乎并不知道他们的歌中有种可怕的气息。(此处可展开描写)
异象十二年,这一批刚刚进入青春期的孩子,开始制造自己的流行文化。他们用发型、衣着与年长自己的人区隔开。那些仅比他们大一两岁的学长学姐们,也有些人受到了这种文化的影响。那些仍然当红的流行明星,成为了他们嘲笑的对象。A(一个女孩)因为他的鼻子长得像Y(一个明星)而倍感苦恼。这种原本被视为漂亮的鼻型在这群孩子种被视为是不自然的。
从异象十五年开始,高中辍学率不断上升,留在学校的学生也对老师教授的所谓知识毫无兴趣。『科学是暴力的工具,与智慧无关』这种奇怪的反智论调在学生中流传。但这并不意味着这些学生是无知的,他们可以随时从互联网上获取自己想要的知识。『我们要自我教育』一些老师们对于这种危险的口号忧心忡忡又无可奈何,更多的教师则认为这不过是年轻人的躁动,不足为奇。这些学生建立起了自己的P2P社交网络,完全匿名,技术上无法监控。有一些校风颇为严格的学校,直接在非教学时间关闭了供电系统,但这激起了学生们的反抗,让人无法理解的是,反抗的积极分子大多是低年级的学生,而高年级的学生则多是观望的角色。
异象十八年,这一年的一流大学的招生遇到了很大的困难,报考者大多资质平平。这些曾经高高在上的学府对此大惑不解。据一名各项测试成绩平平,却轻松进入一流大学的幸运者称,这是他们刻意为之,目的是把水搅混,把这些名牌大学的牌子搞臭,至于为什么要把这些名牌大学的牌子搞臭,他们并没有说,似乎认为这都是公开的秘密了。而那些成绩优异的人,不过这一届的成绩有多少可信之处呢?而那些被认为聪慧敏捷的,则有目的的『占领』那些中低端的学校,
异象二十年的选美结果出炉了,令富豪们大跌眼镜。现在的正常的年轻人,有几个人会参加上一代人创建的选美比赛呢?今年又有几家面向年轻人的时尚公司倒闭了,他们完全搞不明白这些年轻人想要穿什么,似乎他们的设计总是完美的避开了年轻人的审美。这些年轻人总是穿着一些不知名的小品牌的服装,他们甚至以穿着旧衣服、与他人交换服装为时尚。这简直就是要他们的命。『天下皆知美之为美,斯恶矣』一位蓬头垢面,衣着污秽的艺术家如是对记者说。
异象二十二年,一年比一年差的就业形势,到了这一年跌倒了最低点。虽然很多的岗位都已经用机器人代替了人工,但『精英企业家』们依然为了『社会公益』而『创造了大量的就业岗位』。与异象前二十年的人们不同,这届人民似乎丝毫不懂得什么叫做『感恩之心』,若没有他们『创造』出如此多的就业岗位,他们的父母怎么可能有能力供养他们长大。每念及此,年近一百二十岁的企业家老李就倍感痛心。『我还能为社会再干30年』他面对镜头时既谦逊又绅士。
(TODO)
]]>庄子天下篇说,上古之人真是完备啊,可惜后世的人很不幸,他们将看到道术在天下分裂。
古希腊哲学、古印度思想、中国古代思想,有一个更统一的源头。那就是上古太初之人。
我们今天的思想是东西方思想的结合,依然不够完满,缺失了重要的部分。
美洲文化受到了破坏。非洲有一部分上古文化留存,苗瑶族也有一些上古之道留存。但都不完整。
我们的目标应该是恢复一个人类上古之道,而不是自称独立发展的,自绝于世界。
也不是盲目的学习他人,放弃自己,因为他人也不过是缺憾的存在。
人类无论从基因上,还是从思想上,都是残缺的。
我们需要追求圆满自足,这是人所遗忘的功能。
]]>最初我尝试用PG来训练这个看似简单的游戏。每一步,都视为1点奖励,如果失败则给予-1000惩罚。算法很快习得了一个偷懒的方法,每一步都进行无效的移动,以此来苟延残喘。于是将无效的移动操作,视为重大的失误,也同样给予-1000的惩罚。算法很快学会了在一个局面下的有效移动操作。但这个游戏,哪怕只是随机的移动也能够取得一个普通的结果,如果要突破极限,则需要使用一些特殊的策略,我期待算法是否能在训练中学会这些策略。
对于这个游戏,达到2048,需要大约500次移动,达到4096,则需要1000,达到1M,也就大约需要20多万次移动,达到4M,则需要上百万次移动。每到达一个新的难度,面临的局面都不同,之前所习得的经验就不一定继续有效了。2048游戏是一个比围棋要简单很多的游戏,围棋拥有更多的选点,2048只有4个操作选择。他们的主要区别在于围棋一般在100多手内结束,而2048的游戏时间则近乎无限长。理想的游戏结果如下图所示:
这一游戏是存在理想玩法的,经过很多局的游戏,我已总结出一些经验。但是这些经验是感性的,很难使用逻辑规则表达出来,很多时候是凭直觉的。我手段操作达到了512K的结果,虽然我依然可以挑战更高的游戏记录,但显然我不能将如此多的时间浪费在滑动手指上。这也是我要编程解决这个问题的初衷,但是强化学习算法,只能在一次次的失败中得到教训,可是这个游戏的特点是,训练的越好,游戏时间越长,获得失败经验的成本就越大。所以无论该算法在理论上是多么的正确,但在实际操作过程中已经变得不可行了。
DQN、PG等强化学习算法的基本过程是根据系统给予的奖励,努力最大化收益。但是对于一个没有明确获胜终点的系统,如果验证训练结果的有效性却是一个非常大的问题。由于强化学习本质上是通过过往的经验来调整自己的策略的,如果有明显的获胜路径,则算法可以有充分的胜利经验可供借鉴。但如果目标是永远安全的运行下去,没有获胜的路径,只有失败的惩罚,那么算法只能在有限的教训中得到学习。假设我们正在训练一个自动飞行系统,获取每一个经验教训的成本都非常巨大,强化学习在这一方面的应用,必须要搭配一些人类的理性评估作为辅助,但是将人类的意识转化为可以实施的程序逻辑又是非常复杂的事情。
如果我们训练的是一个自动驾驶系统呢?在未来无人驾驶会应用的越来越多。无人驾驶的安全性会很快超越人类,随即人们期望可以进一步提升驾驶的平均速度或其他一些智能驾驶的指标。因为无人驾驶的安全性已经超越了人类,所以无法再依赖于人类的驾驶经验给予其帮助,只能依赖于自身驾驶中的经验(尤其是事故)作为训练依据。那么这时这个系统还可能是安全的吗?
在未来的相关强化学习领域,一个好的环境模拟系统、事故全息信息的采集和共享系统,才是提升人工智能的关键,而不是算法。
]]>如果说中国的贫富差距问题,都是政府的问题,我觉得是不恰当的,中国的贫富差距深层次的原因在于民众的思想。大众在思想上并没有真正的完成社会主义改造,很多人只是迫于形势暂时屈从于社会主义集体思想,但本质上并没有放弃发家致富的思想。依然是用私欲在驱使自己,与那些真正拥有共产主义理想甚至愿意为之献生的人是完全不可以相提并论的。这种思想的不同自然会导致经济制度的不同。
资本主义本质上是一种达尔文主义,也就是物竞天择、适者生存的那一套说辞。其漏洞是把人作为独立个体看待忽略了人的社会性,也忽略了人的思想是可以改变的而基因只能突变这种本质区别,这些本质的区别使达尔文的生物进化过程不能够直接的套用于人类的思想演化过程。生物只能依靠随机的不可控的突变来进化,但是人类可以在伟大的哲人带领下通过思辩来改造大众的思想进而改变社会制度。
历史已经证明,只要一种思想能够被普遍的接受哪怕是错误的思想,都会引起社会巨大的改变,比如各种有神论的宗教。而有些思想即便他是更加正确的,只要不能够被大众接受,也就依然无济于事,比如墨子的思想。孟子曾经说过,杨朱和墨翟两个人的言论充斥天下,天下人要么信奉杨朱的思想,要么信奉墨翟的思想。孟子还说:杨朱为我,是无君也,墨翟兼爱,是无父也,无君无父,是禽兽也。杨朱是讲利己主义的,如果拔他小腿上的一根毛能够有利于天下的话,他也不会拔。杨朱这个人留下的话不多,主要是其他诸子骂了他,所以他有些话留了下来,很明显当时是个显学。列子杨朱篇上记载,有人问,做人干嘛要求名呢?杨朱说:求名为了富。又问:已经富了,为什么还不停止呢?答:为贵。又问:已经贵了,为什么不停止?答:为死。(可能是指为了厚葬)。又问:已经死了还为什么呢?答:为子孙。各位看看,杨朱的思想是不是符合世界上大多数人的想法呢?尤其是当代的中国人,所以诸子们没有不鄙视杨朱的。国人以前比较贫穷,有些人因为贫穷而自卑,以为富起来就会受人尊重,但是最后发现,富起来之后,人家照样瞧不起你。为什么呢?这个问题是值得思考的。有些人则不同,即使贫穷却依然受到全世界的尊敬,比如墨子。
除了孟子骂过墨子,其他人都是尊敬墨子的。我想孟子也不是要骂墨子,而是为了捍卫儒家思想不得已的反击,因为墨子反对礼乐,反对亲疏贵贱。但事实上儒家和墨家有很多共同的价值观,儒家大同社会的理想和墨家思想几乎没什么区别“大道之行也,天下为公。选贤与能,讲信修睦。故人不独亲其亲,不独子其子……是谓『大同』。”,“今大道既隐,天下为家。各亲其亲,各子其子;……是谓『小康』”。这些话是什么意思呢?意思是不是我们不喜欢墨家,而是墨家实现不了,我们只能暂时降低一下要求,追求小康。所以说儒家是一个妥协的理论,但妥协多了,就会出问题。那些读书人,一旦飞黄腾达了,就过上了“精英”的生活,哪里还会有什么大同的理想呢?妥协只能让不合理的制度继续存在下去,直到达到社会承受的极限才会发生革命。当然儒家也是支持汤武革命的。所以儒家思想是既妥协又革命的,这样究竟好不好,这个我们可以讨论。
墨子认为,你要爱你的父母,最好也爱别人的父母,那么别人也会爱你的父母,这样就可以交相利,兼相爱。有些人是认为人都是怎样的,所以我也要怎样。而墨子认为这样更好,所以你应该这样。比如墨子也有天志明鬼的思想,教人信鬼神,墨子是怎么证明的呢?墨子认为人如果信鬼神的话,会有所敬畏,行善去恶,还能联络感情,加强团结。依然是认为信鬼神更好,所以人应该信奉鬼神。墨子的思想都是从对整个社会更有利的角度去分析,然后认为人应该是什么思想的。因为墨子的思想逻辑严谨,加上墨子本人身体力行,所以墨子的门徒全都是信仰坚定,赴汤蹈火,死不旋踵。墨子的组织是可以存在的,但这种组织的存在建立在墨家的共同信仰基础上,如果没有这种共同信仰,这种组织也就无法存在了。简而言之,你相信,他就会实现,你不相信,他就不会实现。庄子天下篇评价墨子,“其生也勤,其死也薄,其道大觳,使人忧,使人悲,其行难为也,恐其不可以为圣人之道,反天下之心,天下不堪。墨子虽能独任,奈天下何!”。他认为,墨子的道太惊人了,使人担忧,又使人悲哀,与天下人的想法都是相反的,墨子你虽然能吃得了那些苦,对天下又有什么办法呢?
庄子的评价不是没有道理,天下人不信墨子,害怕受墨子之苦,那么天下人的结局是什么呢?不受墨子之苦,就要受秦法之苦。商君有法,墨子也有法。商鞅的法是等级制,墨子的法是平均制、供给制。(引用一个故事:墨者巨子腹,居秦,其子杀人。惠王曰:‘先生年长矣,非有他子也,寡人已令吏勿诛矣。’腹对曰:‘墨者之法,杀人者死,伤人者刑,王虽为赐,腹不可不行墨者之法。’遂杀其子) 商鞅和墨子没有能够相互辩论,但是曾经有两位湖南人就此事发生过巨大的分歧。
人们没有选择墨子之法,选择了商君之法,秦国一统六国。对商君之法不满意,高祖斩白蛇而起义。汉初信奉道家的自由主义,自由主义导致豪族逐渐产生,国力削弱。汉武帝随后又开始拿出妥协的儒家,无论是举孝廉还是九品中正,还是科举,但依然向着门阀社会不可逆转的滑落了下去,虽有赤眉起义、黄巾起义依然不能阻止。然后是五胡乱华,几乎是亡国灭种。按照天下人心自然发展下去,结果就是如此。西晋的石崇富可敌国,名士王导和王敦去他家作客,石崇让美人劝酒,如果客人不喝,石崇就会杀掉美人。王敦根本不管这一套,就是不喝,石崇连杀了三个美人,他仍然不喝。王导责备王敦,王敦说:“他杀他家的人,和你有什么关系?”人已经彻底沦为了奴隶,成了某些人的私有财产,而这些知识分子也对此是无动于衷的。
墨子被人遗忘了很多很多年,统治者绝对不会提起墨子。商君之法和儒家的妥协,可以保证社会稳定几百年,然后再重来一次。天下人总是不愿意放弃自己的幻想,以为自己有一天也可以王侯将相,或者如石崇富可敌国,抑或如王谢名士风流,只有发现权、利、名都与自己无关时,依然不能醒悟。其实他们不知道,就算是石崇等人,也都没有什么善终,为什么还要抱这种幻想呢?无论是举孝廉还是科举制,无论提供怎样的晋升阶梯,这种旧制度的修修补补都进一步延长了这种旧制度。直到一场全球范围的更大的战国乱世出现,才有人提出了和墨子类似的主张,那就是共产国际。但是,只要天下人心未变,墨子之道,就永远没有实现的一天,人类就必须困在相互倾轧的循环中万劫不复。
]]>人为装饰越多,问题越多。人为装饰导致一个错误的基因得到了遗传。
天启宗教是现代文明的伴生文化。
现在继承中国传统文化,你继承和恢复的可能不是中华文化,而是中华文化的伴生文化。
]]>四十年前,有人提出要全面的理解伟人的思想,并在伟人著作的选集中增加一篇《反对本本主义》,并强调“实践是检验真理的唯一标准”,当然“实践论”也依然是伟人的名篇,但说法却不是这个说法,《实践论》强调的是“理论要结合实际,经验是理论的前提”是有很深的哲学方法论的,但是“实践是检验真理的唯一标准”这句话则有着强烈的成王败寇色彩。但必须承认这些话都是非常雄辩的,观点是非常有力的。于是受此启发,也就写了这篇“完整的理解道家思想和达尔文主义”。我认为,道家思想和达尔文主义是右派的最根本的理论基础,这是他们雄辩的部分,而他们错误的部分则是未能完整的理解道家思想和达尔文主义。
儒生和道家的争论。。。。汉景帝也是很没出息,他应该说如果朕做出违背人民的事情,也会受到一样的下场。这个道生的意思,就是君王再坏也是君王,臣下也不能弑君。这个感觉就像,有钱就是厉害,贪污也是人家的本事,口气是不是很像?就很明显没有真正理解道德经,汤武革命正是天地不仁以万物为刍狗的合理性结果。而圣人不仁所以百姓为刍狗的百姓,显然是包含贵族在内的,那时如果说下等人那叫做黎民,不叫百姓,有姓的都是贵族。所谓以百姓为刍狗,就是不管你是王公贵族还是黎民百姓,在圣人眼中都是刍狗,都是平等的。我这么解释也许有人会认为我是附会老子的意思,那么我们看看庄子的思想是什么。庄子是道家仅次于老子的重要人物。庄子在齐物论充分表达了什么叫做以万物为刍狗,那就是万物都是平等的,没有高低贵贱之别,这样怎么会存在一个不可以被推翻的君主呢?
社会达尔文主义的普遍错误。
进化论的翻译有问题,应该翻译为演化论。
演化论的意思是,把你丢到大草原上,你要么成为狮子,要么成为鬣狗,或者成为斑马,把你丢到猪圈里面,你必须变成一只猪才是适应环境的。把你丢到猪圈里面,你还想做人可以吗?按照演化论的规律来说,你在猪圈里面做人,就会饿死。演化论没有错,但把在猪圈里面做猪当作理想社会,那就是你的错了。
庄子在达生篇有一个小寓言,祭祀官穿着黑色礼服,一本正经地来到猪圈,劝说猪:“你干嘛厌恶死亡呢?我要饲养你三个月,然后戒十天,斋三天,用白茅作垫席,准备一张雕绘精美的案几,把你摆在上面去祭祀神灵,那么你愿意做吗?”替猪着想的话,一定会说,这样还不如拿糟糠喂养它,把它关在猪圈里呢;为人自身考虑的话,假如活着能享有高官厚禄的尊贵,死了能用华丽的装载灵柩的车子拉着,下葬时享受豪华的棺椁,那么就会去做。替猪着想的话,那就抛弃白茅彫俎;为自己打算就拼命追求轩冕腞楯聚偻。人与猪的不同之处是什么呢?庄子的意思很明显,追求自由,不是让你们在那里自我摧残的,连猪都知道不该追求那些虚头巴脑的东西,人干嘛要整那些没用的呢?
道家思想从来都是讲为而不争,而不是自由竞争。什么叫为而不争,我觉得和孔子说的君子和而不同,是一个意思。孔子说,君子和而不同,小人同而不和。小人都是一个样的,整体就知道争。君子呢,努力做事,但是从来不争。
丛林法则的有效的基础是绝对公平的游戏规则。基因的进化是不可以父传子的,更是不可以窃取公器的。达尔文主义是个体的竞争,田径比赛,高考,并且要禁止开小灶,才能达到优选优育的目的。公平是进化论的基础。关于生育部分也就不多说了,达尔文主义也要求一部分人应该多生育。基督教的一夫一妻制根本就不符合进化论的思想。在一夫一妻制下谈社会达尔文主义是没有意义的,因为你再怎么竞争也没有扩大你的遗传优势,没有遗传优势也就没有所谓演化。
优胜劣汰是市场经济的常用话语,但究竟是优胜劣汰还是劣币驱逐良币,我觉得要一分为二的看。大量的事实已经证明在没有强力监管的情况下是劣币驱逐良币而不是优胜劣汰。比如说,环境污染,那肯定是不治理污染的成本低,驱逐主动治理污染的,抄袭盗版的成本低驱逐自主研发的,无视劳工人权的成本低驱逐福利优厚的,官商勾结的成本低驱逐合法经营的。
人与动物最大的区别之一就是人可以制定规则,也可以改变规则,但动物无法改变人类给他们制定的规则。既然人有了动物所不具有的制定规则的能力,那么人类就应该制定一个属于人类的规则,而不是给人类制定一个动物的规则。
老子强调,为而不有,功成而弗居,老子是反对私有财产的,老子也是反对物质享受的。那么消费主义就不符合道家思想。
完整的理解猫论。不管白猫黑猫抓到老鼠就是好猫。这里的猫和老鼠究竟指什么?白猫黑猫是计划与市场,老鼠就是目标,这个目标是什么?这个目标是社会主义。让一部分人先富起来,这个目标就是最终实现共同富裕。如果你认为老鼠是一部分人先富起来,那么就大错特错了。
为什么闲的没事写这些玩意儿呢?因为市场竞争挺无聊的,以我们这些智商和那些蝇营狗苟的人竞争,一群野狗争抢食物,对外杀敌的时候,没多少输出,抢起食物来却非常起劲,我们的力量是用来对外的,不是用来对内的。还是老子那句话,为而不争。搞内部倾轧的竞争,厚黑学,对有修养的人来说,是很丢脸的一件事。
孔孟为什么伟大,因为孔孟肯定了革命的合法性。为什么要批孔呢?因为儒家已经被后生所篡改,将忠君变成了儒家教条的一部分,其实儒家一直认为民为贵、社稷次之、君为轻,而且肯定汤武革命,既然肯定汤武革命就不可能把忠君作为教条,即便董仲舒的天人感应,五德终始,也是劝喻君王,不好好干就要下台。但是后来儒家被偷换概念,成为了精英的代名词,这个转换与科举制有一定关系。也就是通过垄断知识来垄断权力,皇权与知识分子结成了牢固的联盟,这个在宋朝发展到了顶点。所以文革批孔,批的其实是精英意识,而不是批的革命意识。
汤武当然可以革命!
今天的新儒学复兴,一定要注意甄别,复兴的是精英意识还是革命意识,如果是精英意识,那么就要毫不留情的打倒他,如果是革命意识,那么说明就领悟到了儒家的精髓,就可以拥护他。
明朝时,儒生们积极的评议朝政,只可惜明朝仍然有很多制度上的缺陷,还是覆亡了。明朝覆亡后,孔孟的精神没有消失,西移到了法国。
]]>1 | class PolicyNet(nn.Module): |
不要忘了输出层的SoftMax。
相对于DQN,我们也不需要额外的目标网络和参数复制操作,只需要一个策略网络即可。
1 | BATCH_SIZE = 256 |
在选择动作时,我们不再需要特地设置探索概率,因为输出结果就是各个动作的概率分布。我们使用torch.distributions.categorical.Categorical
来进行取样。在每次选择动作时,我们同时记录对应的概率,以便后续使用。这个概率就是 `ln pi_theta(S_t,A_t)`
1 | log_probs = [] |
为了更新参数,我们首先需要计算`v_t`,这在后续参数迭代中需要用到。
在模拟执行的时候,我们记录了每一步的reward,我们需要计算每一步的`v_t`,其顺序与执行顺序一致。根据公式我们需要倒序的计算`v_t`,然后将计算好的结果倒序排列,就形成了`v_1,v_2…v_t`的序列。最后我们需要将数据标准化。(TODO: 这里可能存在一个序列对应的问题,其中每一个状态的累计收益,是后续状态收益之和,不包含本轮收益)
1 | values = [] |
接下来我们需要更新参数,参数更新的公式为:
我们将其转换为损失函数形式:
这个损失函数的形式可以帮助我们更好的理解策略梯度的原理。如果一个动作价值为负值,但是其选择概率为正,则损失较大。
1 | loss = [] |
训练循环需要在一局结束之后进行。并清除rewards、log_probs缓存。对于cartpole-v1环境,要注意他的每一步奖励都是1,很显然在最后一步代表着游戏失败,我们需要施加一定的惩罚,我们将最后一步的奖励设为-100。
1 | num_episodes = 5000 |
因为这个网络与策略函数的定义一样,所以被称为策略网络。`pi_theta(a|s)`,表示在`s`状态下选择动作`a`的概率。只要这个网络能够收敛,我们就可以直接得到最佳策略。这个网络的奖励函数也就是最终游戏的总奖励。
`J(theta) = sum_(s in S)d^pi(s)V^pi(s) = sum_(s in S)d^pi(s)sum_(a in A)pi_theta(a|s)Q^pi(s, a)`
`d^pi(s)`指状态`s`在马尔科夫链上的稳定分布,`d^pi(s) = lim_(t->oo)P(s_t=s|s_0,pi_theta)`。
但是这个表达式看上去是不可能计算的,因为状态的分布和Q值都是随着策略的更新而不断变化的。但是我们并不需要计算`J(theta)`,在梯度下降法中我们只需要计算梯度`grad_(theta)J(theta)`即可
`grad_(theta)V^pi(s)`
`= grad_(theta)(sum_(a in A)pi_theta(a|s)Q^pi(s, a))`
根据导数乘法规则
`= sum_(a in A)(grad_(theta)pi_(theta)Q^pi(s, a)+pi_(theta)(a|s)grad_thetaQ^pi(s, a))`
展开`Q^pi(s,a)`为各各种可能的下一状态奖励之和
`= sum_(a in A)(grad_(theta)pi_(theta)Q^pi(s, a)+pi_(theta)(a|s)grad_(theta)sum_(s’,r)P(s’,r|s,a)(r+V^pi(s’)))`
而其中状态转移函数`P(s’,r|s,a)`、奖励`r`由环境决定,与`grad_theta`无关,所以
`= sum_(a in A)(grad_(theta)pi_(theta)Q^pi(s, a)+pi_(theta)(a|s)sum_(s’,r)P(s’,r|s,a)grad_(theta)V^pi(s’))`
`= sum_(a in A)(grad_(theta)pi_(theta)Q^pi(s, a)+pi_(theta)(a|s)sum_(s’)P(s’|s,a)grad_(theta)V^pi(s’))`
现在我们有了一个形式非常好的递归表达式:
`grad_(theta)V^pi(s) = sum_(a in A)(grad_(theta)pi_(theta)Q^pi(s, a)+pi_(theta)(a|s)sum_(s’)P(s’|s,a)grad_(theta)V^pi(s’))`
设 `rho^pi(s->x, k)` 表示在策略`pi^theta`下,`k`步以后状态`s`转移到状态`x`的概率。有:
为了简化计算,令 `phi(s)=sum_(a in A)grad_(theta)pi_theta(a|s)Q^pi(s,a)`
`grad_(theta)V^pi(s)`
`= phi(s) + sum_(a in A)pi_(theta)(a|s)sum_(s’)P(s’|s,a)grad_(theta)V^pi(s’) `
`= phi(s) + sum_(s’)sum_(a in A)pi_(theta)(a|s)P(s’|s,a)grad_(theta)V^pi(s’) `
`= phi(s) + sum_(s’)rho^pi(s->s’,1)grad_(theta)V^pi(s’) `
`= phi(s) + sum_(s’)rho^pi(s->s’,1)(phi(s’) + sum_(s’’)rho^pi(s’->s’’,1)grad_(theta)V^pi(s’’)) `
`= phi(s) + sum_(s’)rho^pi(s->s’,1)phi(s’) + sum_(s’’)rho^pi(s->s’’,2)grad_(theta)V^pi(s’’) `
`= phi(s) + sum_(s’)rho^pi(s->s’,1)phi(s’) + sum_(s’’)rho^pi(s->s’’,2)phi(s’’) + sum_(s’’’)rho^pi(s->s’’’,3)grad_(theta)V^pi(s’’’) `
`= …`
`= sum_(x in S)sum_(k=0)^(oo)rho^pi(s->x, k)phi(x)`
令 `eta(s)=sum_(k=0)^(oo)rho^pi(s_0->s, k)`
`grad_(theta)J(theta)=grad_(theta)V^pi(s_0)`
`= sum_(s)sum_(k=0)^(oo)rho^pi(s_0->s,k)phi(s)`
`= sum_(s)eta(s)phi(s)`
`= (sum_(s)eta(s))sum_(s)((eta(s))/(sum_(s)eta(s)))phi(s)`
因 `sum_(s)eta(s)` 属于常数,对于求梯度而言常数可以忽略。
`prop sum_(s)((eta(s))/(sum_(s)eta(s)))phi(s)`
因 `eta(s)/(sum_(s)eta(s))`表示`s`的稳定分布
`= sum_(s)d^pi(s)sum_a grad_(theta)pi_(theta)(a|s)Q^pi(s,a)`
`= sum_(s)d^pi(s)sum_a pi_(theta)(a|s)Q^pi(s,a)(grad_(theta)pi_(theta)(a|s))/(pi_(theta)(a|s))`
因 ` (ln x)’ = 1/x `
`= Err_pi[Q^pi(s,a)grad_theta ln pi_theta(a|s)]`
所以得出策略梯度最重要的定理:
` grad_(theta)J(theta)=Err_pi[Q^pi(s,a)grad_theta ln pi_theta(a|s)] `
其中的`Q^pi(s,a)`也就是状态s的累计收益,可以在一次完整的动作轨迹中累计计算得出。
该算法被称为 REINFORCE
对于这个环境,尝试了很多次,总是不能达到很好的效果,一度怀疑自己的代码写的有问题。后来仔细看了这个环境的奖励,是每一帧返回奖励1,哪怕是最后一帧也是返回1 的奖励。这里很明显是不合理的俄。我们需要重新定义这个奖励函数,也就是在游戏结束的时候,给一个比较大的惩罚,r=-100。很快可以达到收敛。
1 | Transition = namedtuple('Transition', ('state', 'action', 'next_state', 'reward')) |
1 | class DQN(nn.Module): |
1 | import gym |
1 | steps_done = 0 |
这里主要是抽样、目标值计算、损失计算的部分。损失计算采用Huber loss。
1 | def optimize_model(): |
这里主要有主循环、获取输入、记录回放、训练、复制参数等环节。
1 | num_episodes = 1000 |
Q表存储着状态s和动作a、奖励r的信息。我们知道深度神经网络,也具有存储信息的能力。DQN算法就是将Q-table存储结构替换为神经网络来存储信息。我们定义神经网络`f(s, w) ~~ Q(s)`,输出为一个向量`[Q(s, a_1), Q(s, a_2), Q(s, a_3), …, Q(s, a_n)]`。经过这样的改造,我们就可以用Q-learing的算法思路来解决更复杂的状态空间的问题了。我们可以通过下面两张图来对比Q-learning和DQN的异同。
网络结构要根据具体问题来设计。在神经网络训练的过程中,损失函数是关键。我们采用MSE来计算error。
`L(w) = (ubrace(r + argmax_aQ(s’, a’))_(目标值) - ubrace(Q(s, a))_(预测值))^2`
1 | 使用随机参数初始化网络Q |
但是,通过实验我们会发现,训练过程非常的不稳定。稳定性是强化学习所面临的主要问题之一,为了达到稳定的训练我们需要运用一些优化的手段。
Agent生活在环境之中,并根据环境的反馈进行学习,但环境是否是稳定的呢?假设agent在学习出门穿衣的技能,它需要学会在冬天多穿,夏天少穿。但是这个agent只会根据当天的反馈来修正自己的行为,也就是说这个agent是没有记忆的。那么这个agent就会在多次失败后终于在冬天学会了多穿衣,但转眼之间到了夏天他又会陷入不断的失败,最终他在夏天学会了少穿衣之后,又会在冬天陷入失败,如此循环不断,永远不会收敛。如果要能够很好的训练,这个agent至少要有一整年的记忆空间,每一批都要从过去的记忆中抽取记忆来进行训练,就可以避免遗忘过去的教训。
在DeepMind的Atari 论文中提到
First, we used a biologically inspired mechanism termed experience replay that randomizes over the data, thereby removing correlations in the observation sequence and smoothing over changes in the data distribution.
意思是,受生物学启发,他们采用了一种叫做经验回放(experience replay)的机制,随机抽取数据来到达“移除观察序列的相关性,平滑数据分布的改变”的目的。
我们已经理解了要有经验回放的记忆,但是为什么一定要随机抽取呢?对此论文认为这个随机抽取可以移除序列相关性、平滑分布中的改变。当如何理解呢?简单的说就是在我们不清楚合理的周期的情况下,能够保证采样的合理性。我们仍然以四季穿衣举例,假设我们不使用随机采样,我们必须在每次训练中都采用365天左右的数据,才能使我们的数据样本分布合理。可是agent并不清楚一年365天这个规律,这恰恰是我们所要学习的内容。采用随机采用,就可以自然的做到数据的分布合理,而且只需要使用记忆中的部分数据,减少单次迭代的计算量。
在这个记忆里,我们并不记录当时的网络参数(分析过程),我们只记录(状态s,动作a,新状态s’, 单步奖励r)。显然,记忆的尺寸不可能无限大。当记忆体增大到一定程度之后,我们采用滚动的方式用最新的记忆替换掉最老的记忆。就像在学习围棋的过程中,有初学者阶段的对局记忆,也有高手阶段的对局记忆,在提升棋艺的角度来看,高手阶段的记忆显然比初学者阶段的记忆更有价值。
说句题外话,其实对于一个民族而言也是一样的。我们这个民族拥有一个非常好的传统,就是记述历史,也就是等于我们这个民族拥有足够大的记忆量,这是我们胜于其他民族的。但是这个历史记录中,掺杂了历史上不同阶段的评价,这些评价是根据当时的经验得出的。而根据DQN的算法描述来看,对我们最有价值的部分其实是原始信息,而不是那些附加在之上的评价,这些评价有正确的部分,也有错误的部分,我们不用去过多关心。我们只需要在今天的认知(也就是最新的训练结果)基础上,对历史原始信息(旧状态、动作、新状态、单步奖励)进行随机的抽样分析即可。
DQN另一个稳定性问题与目标值计算有关。因为`target = r + gamma * argmax Q(s’)`,所以目标值与网络参数本身是相关,而参数在训练中是不断变化的,所以这会造成训练中的不稳定。一个神经网络可以自动收敛,取决于存在一个稳定的目标,如果目标本身在不断的游移变动,那么想要达到稳定就比较困难。这就像站在平地上的人很容易平衡,但如果让人站在一个不断晃动的木板上,就很难达到平衡。为了解决这个问题,我们需要构建一个稳定的目标函数。
解决的方法是采用两个网络代替一个网络。一个网络用于训练调整参数,称之为策略网络,另一个专门用于计算目标,称之为目标网络。目标网络与策略网络拥有完全一样的网络结构,在训练的过程中目标网络的参数是固定的。执行一小批训练之后,将策略网络最新的参数复制到目标网络中。
经验回放和目标网络的效果见下表(引用自Nature 论文):
关于DQN的优化,这篇文章描述的比较全面 https://zhuanlan.zhihu.com/p/21547911。在之后的实践中考虑是否进一步深入。主要介绍3个改进:
gym
来作为我们对实验环境。安装方法:1 | pip install gym |
我们的实验环境是一个冰湖滑行游戏,你将控制一个agent在冰面到达目标终点,前进方向并不总受你的控制,你还需要躲过冰窟。
1 | import gym |
游戏画面示意如下:
1 | SFFF (S: 起点,安全) |
1 | import random |
这是一个Agent的一般结构,主要由初始化、选择动作、更新状态变化,三个方法构成。后续的其他算法将依然采用该结构。q表数据使用一个二维数组表示,其大小为 state_count action_count,对于这个项目而言是一个 `16*4` 的大小。
1 | # 返回状态s的最佳动作a、及其r值。 |
1 | def choose_action(self, s): |
我们进行10000局游戏的训练,每局游戏执行直到完成。
1 | env = gym.make('FrozenLake-v0') |
在测试中,我们只选择最佳策略,不再探索,也不再更新Q表。
1 | # 获胜次数 |
最终测试的效果是在1万局中获胜了7284次,说明达到了不错的实验效果。
]]>我们可以考虑一个最简单的环境:一个动作可立刻获得奖励,目标是使每一个动作的奖励最大化。对这种单步强化学习任务,可以设计一个理论模型——“K-摇臂赌博机”。这个赌博机有K个摇臂,赌徒在投入一个硬币后可一选择按下一个摇臂,每个摇臂以一定概率吐出硬币,但这个概率赌徒并不知道。赌徒的目标是通过一定的策略最大化自己的奖赏,即获得最多的硬币。最终所获得的总奖励被称为累计奖励。
对于这个简单模型,若要知道每个摇臂的概率,我们只需要进行足够多的尝试即可,这是“仅探索”策略;若要奖励最大化,则需要执行奖赏概率最大的动作即可,这是“仅利用”策略。但在更复杂的环境中,我们不可能对每一个状态的每个动作都进行足够多的探索。比如围棋,我们后续的探索是需要依赖于之前的探索的。因此我们需要在探索和利用之间进行平衡。我们在学习的过程中,必须要保持一定的概率`epsilon`进行探索,其余时候则执行学习到的策略。
为了便于分析讨论,我们定义一些术语。
机器agent处于环境`E`中。
状态空间为`S`,每个状态`s in S`是机器感知到的环境描述。
机器能够采取的可采取的动作a的集合即动作空间`A`,`a in A`。
转移函数`P`表示:当机器执行了一个动作`a`后,环境有一定概率从状态`s`改变为新的状态`s’`。即:`s’=P(s, a)`
奖赏函数`R`则表示了执行动作可能获得的奖赏`r`。即:`r=R(s,a)`。
环境可以描述为`E=<<S, A, P, R>>`。
强化学习的任务是习得一个策略(policy)`pi`,使机器在状态`s`下选择到最佳的`a`。策略有两种描述方法:
累计奖励指连续的执行一串动作之后的奖励总和。
`Q^(pi)(s, a)`表示在状态`s`下,执行动作`a`,再策略`pi`的累计奖励。为方便讨论后续直接写为`Q(s,a)`。
`V^(pi)(s)` 表示在状态`s`下,使用策略`pi`的累计奖励。为方便讨论后续直接写为`V(s)`。
强化学习往往不会立刻得到奖励,而是在很多步之后才能得到一个诸如成功/失败的奖励,这是我们的算法需要反思之前所有的动作来学习。所以强化学习可以视作一种延迟标记的监督学习。
对于我们要学习的`Q(s,a)`函数,我们可以使用一个Q-table来表示。Q-table是一个二维表,记录每个状态`s in S, a in A`的`Q`值。Q表被初始化为0的状态。在状态`s`执行了动作`a`之后,得到状态`s’`,奖励`r`。我们将潜在的`Q`函数记为`Q_(real)`,其值为当前奖励r与后续状态`s’`的最佳累计奖励之和。则有:
` Q_(real)(s, a) = r + gamma * argmax_aQ(s’, a) `
` err = Q_(real)(s, a) - Q(s, a) `
其中`gamma`为`Q`函数的偏差,`err`为误差,`alpha`为学习率。 可得出更新公式为:
` Q(s, a) leftarrow Q(s, a) + alpha*err `
即:
` Q(s,a) leftarrow (1-alpha)Q(s,a) + alpha(r + gamma * argmax_aQ(s’, a)) `
以上公式即为Q-learning的关键
1 | # 初始化Q表 |
下图是一个Q表内存结构,在经过一定的学习后,Q表的内容将能够不断逼近每个状态的每个动作的累计收益。
美国会倒下,但我们不知道美国将会如何倒下。中美最根本的战争在金融领域,人民币国际化就是在争夺全球铸币权份额。美元可能会崩溃,但中国拥有的大量美元资产需要安全置换。如果美元霸权能够缓缓的衰落,对中国的影响最小,如果美元霸权骤然终结,也将对中国带来冲击。
在现在的国际环境下,依靠出口拉动经济的方式是不可持续的,最重要的是提振内需,但目前老百姓手里没钱,内需也拉不动。所以现在必须要平衡贫富差距。缩小贫富差距就要有人掏钱,因此有钱的人都想着抽逃资金。严打不仅稳定社会治安,也会收回大量不法收入。
房价不会大涨也不会大跌,上涨会让还没买房的人不满意,下跌会让已经买了房的人不满意,所以就锁住,不涨不跌。房子已经进入了计划时代,就像火车票必须凭身份证购买来抑制黄牛是一样的。信息化使计划经济具有了更大的可行性。即使通货膨胀了,房价也不会涨,因为已经不再是市场化环境了。所以本质上,房价在缓缓下跌。
需要优化财富分配,医疗养老之类的福利会加强,教育科研投入会加大,贫困人口持续扶贫。
大量高科技依然掌握在欧美手中。中国的普及教育很好,但是高等教育与欧美差距较大,教育改革也是困难重重。仅从本人专业来看,编程语言、操作系统、深度学习框架,都是美国人的。
生产过剩、人工智能,会影响就业率。新增人口下滑,老龄化加速。
创业会越来越难,基本上属于解决就业的公益事业。体制内、大平台的职工相当于新的铁饭碗,同时临时工也会越来越多。
创业的最佳阶段是GDP高速增长的阶段,只要入局基本都有得赚。如果宏观上的财富没有增加,那么创业相当于是在存量市场与其他人竞争。除非有压倒性的武器,找到可以战胜的敌人,否则不要创业。海外市场机会较大。
创新是有闲阶级特权,有闲不一定是非常富有,但需要有一份保证基本生活的、时间投入少的收入,即“睡后收入”。如果创新可立刻获得回报,则可以进入增强回路循环。
随着机器人取代越来越多的低端就业,UBI即无条件基本收入的概念会被越来越多的人接受。今年参加美国总统竞选的杨安泽使用了UBI的口号,别人问他钱从哪来,他说向富人收税。桑德斯再次参加大选,美国民主社会主义近年快速崛起。
传统发达国家因为后发国家的追赶,导致本国产业受到影响,就业率下滑。从而产生越来越多的社会运动,希望获得更高的社会福利,有的地方甚至为了几毛钱的地铁涨价就闹了起来。但这些国家由于债务压力已经很大,经济环境恶化,无法给到更多的社会福利。富人们则利用国际避税手段,来躲避社会责任。一场全球性的萧条和左派革命正在酝酿,那些小政府的发达地区问题最严重(比如香港、韩国是小政府、新加坡是大政府)。
中美的竞争不会转化为热战,因为中国不想打,美国打不赢。中美握手后,将联手打击国际避税,共治天下。
资本因其可以转移,在国际共运中,虽然一部分资产被没收,但更多的则逃到了避风港中。苏联解体、改革开放也使大量的公有资产再次被私有化。美国衰落将使资本最大的避风港消失,所有的资本都将处于监管之下。
中国国内思想分歧增大。近几十年的高速增长,右派认为前人有罪,自己有功,而问题则留给后人处理,大力宣传,所以右派思想居主流。近十年文革一代领导人上台,左派思想逐渐兴起,加之贫富差距、环保、贪腐等问题的存在,助推了左派思潮的壮大。我也是因为近几年的反腐、扶贫、强军才开始重新认识这个国家。
如果说这一代领导人的思想是在文革中形成的话,那么下一届领导人的思想又是在何时形成的呢?文革给中国续了命,修正主义则给苏联送了命。戈尔巴乔夫说“我们是苏共二十大的孩子,苏联六十年代的历史对我们影响很大”,六十年代的苏联就相当于八十年代的中国,赫鲁晓夫教出了戈尔巴乔夫,戈尔巴乔夫一手送走了苏联。下一届领导人是左还是右?改革开放中形成的利益集团是否已经可以左右政治格局,形成类似日韩的财阀统治?这是未来最大的不确定因素。
正因为这是未来最大的不确定因素,所以伟人在晚年才要发动文革。不断受人非议的文革,完成了一代人的政治教育,奠定了一代人的思想基础,顶住了世界范围的社会主义颠覆潮。伟人看得太远了,我们无法企及。但是我们虽然顶住了颠覆,却也经历了改革,大量国有资产被私有化,形成了利益集团。几十年过去了,人心不古。既得利益者一定会想方设法垄断政治权力,不加控制结局与明朝无异。解决方案伟人都说过了,而我们首先要做的就是正确评价文革。反对文革的人常拿文革中的极端案例来举例,犯罪案件每个时代都有,我们要注意到文革时期的犯罪率是远低于改革之后的,即便把群众运动中极端案件算上,也比改革之后的犯罪率低。就连当时“受迫害”的人都没有反对文革(比如现任领导人),那些不了解文革的人又凭什么反对呢?妖魔化文革的本质就是妖魔化群众运动,从而顺理成章的剥夺了民众的政治权力。
扯远了,看来可以写的主题有很多。
]]>pytorch 损失函数的基本用法
1 | criterion = LossCriterion(参数) |
Mean Absolute Error
torch.nn.L1Loss
Measures the mean absolute error.
nn.L1Loss
很少使用
nn.MSELoss
针对数值不大的回归问题。
nn.SmoothL1Loss
它在绝对差值大于1时不求平方,可以避免梯度爆炸。大部分回归问题都可以适用,尤其是数值比较大的时候。
torch.nn.NLLLoss,一般与 LogSoftmax 成对使用。使用时 loss(softmaxTarget, target)
。用于处理多分类问题。
1 | m = nn.LogSoftmax(dim=1) |
nn.CrossEntropyLoss 将 LogSoftmax 和 NLLLoss 绑定到了一起。所以无需再对结果使用Softmax
1 | loss = nn.CrossEntropyLoss() |
二分类问题的CrossEntropyLoss。输入、目标结构是一样的。
1 | m = nn.Sigmoid() |
常用户增强学习、对抗生成网络、排序任务。给定输入x1,x2,y的值是1或-1,如果y==1表示x1应该比x2的排名更高,y==-1则相反。如果y值与x1、x2顺序一致,那么loss为0,否则错误为 y*(x1-x2)
y的值是1或-1,用于衡量两个输入是否相似或不相似。
给定两个输入x1,x2,y的值是1或-1,用于衡量x1和x2是否相似。
其中cos(x1, x2)表示相似度
大多数情况Adam能够取得比较好的效果。SGD 是最普通的优化器, 也可以说没有加速效果, 而 Momentum 是 SGD 的改良版, 它加入了动量原则. 后面的 RMSprop 又是 Momentum 的升级版. 而 Adam 又是 RMSprop 的升级版. 不过从这个结果中我们看到, Adam 的效果似乎比 RMSprop 要差一点. 所以说并不是越先进的优化器, 结果越佳.
1 | # SGD 就是随机梯度下降 |
Kernel,卷积核,有时也称为filter。在迭代过程中,学习的结果就保存在kernel里面。深度学习,学习的就是一个权重。kernel的尺寸越小,计算量越小,一般选择3x3,更小就没有意义了。
结果是对卷积核与一小块输入数据的点积。
所有位置的点积构成一个激活层。
如果我们有6个卷积核,我们就会有6个激活层。
上图是每次向右移动一格,一行结束向下移动一行,所以stride是1x1,如果是移动2格2行则是2x2。
Padding的作用是为了获取图片上下左右边缘的特征。
卷积层为了提取特征,但是卷积层提取完特征后特征图层依然很大。为了减少计算量,我们可以用padding的方式来减小特征图层。Pooling的方法有MaxPooling核AveragePooling。
推荐看一下李飞飞的这篇slide
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode=’zeros’)
torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
torch.nn.AvgPool2d(kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True, divisor_override=None)
Tensor.view(*shape) -> Tensor
1 | >>> x = torch.randn(4, 4) |
MNIST 数据集的输入是 1x28x28 的数据集。在实际开发中必须要清楚每一次的输出结构。
1 | import torch.nn as nn |