2015-12-20

明镜止水

下午等考科目三的时候想到的一个问题.

给定一定的skill level k,结果的pass的score s与否应该是一个类似:
s = k*p
的关于p的一个概率关系.

也就是固有的技能熟练程度随一个分布的"随机"波动.

考虑把概率p展开为加权w的因素f的多项式看的话.
即类似:
p = w_1*f_1 + w_2*f_2 + ... = \sum w_i*f_i

则s=k*p
->
s = k*\sum w_i*f_i

这个形式好像没什么意义.

但如果换个思路写成
s = k + w_i*f_i + w_2*f_2 + ...
的话,即在给定技能水平的条件下,最终结果取决于跟其他因素的一个某种形式的和.

实际形式可能是
s = c_0(k) + c_1(w_i*f_i) + c_2(w_2*f_2) + ...
的形式.
其中c_n为某种到s的space的mapping functions.

通俗地说,就是给定技能水平的话,实际的结果收到一些f因素的影响.
也就是对分布p的波动性的某种形式的解释.

一个前提假设.
如果是绝对理性/理想的情况下,那么给定一定的k,则有一个相对于的固定的s.

现在引入c(w,f)作为对p的解释的话.
也就是说s的最终取值收到参考因素的影响/贡献.

实际的例子可能是f为紧张程度,或者环境因素.
对s的影响的形式就是比如过于紧张/环境关系造成的发挥失常/超常等的一种波动性的解释.

也就是当c(w,f)为positive的时候,对s的贡献就是正的.
换句话说,就是这个因素对技能水平的观测/评测有着积极的/超常的作用.

反之类似.

那么单从等式的角度来说,就存在一些解,使得最终的s尽可能地最大化.

即,如果能够分清因素对"发挥"这种行为是正面还是负面影响的话,就有可能使得最终的结果收益尽可能地大.

当然,这里c(w,f)可能隐含一种相互的约束.
因为s应该是一个bounded的,所以c(w,f)应该也是bounded的.
基于在这里认为c_0(k)是一个绝对中立/理性的值.

但是实际来说,在结果出来直接可能很难判定一个因素的影响是积极的还是消极的.
就像"紧张"这种因素.

它可能使得人注意力分散,也可能反过来刺激人的肾上腺素对结果造成正面的影响.

也就是说,这种描述更多地像是对一个one shot的东西做的一个事后的补充解释.
而不能在事前发现和重复利用.

类似于可以事后给出一个形如:
s = c_0(k) + c_1(w_i*f_i) + c_2(w_2*f_2) + ...
的表达式.
其中c_n都是"已知"的表示.

即可以有任意的满足
s = s' = r(k,w_1,f_1,w_2,f_2...)的函数.

即使在当时能够分辨地出能够影响结果的因素的f_n.
那么依然有着w_n的free variable.
并不能够确切地做一个确定的描述.

如果有确切的描述的话,那么就是已知w_n和k.
最大化s的问题就变成调整因素f_n的问题了.

也就是相对可控的"紧张"到什么程度能让结果较优/最优.

问题是,实际上并不能在事前得到这么一种表达方式.

换个思路.

既然假设了c_0(k)是一个常量的话.
也就是假设技能水平是固定不变的.

那么对于
s = c_0(k) + c_1(w_i*f_i) + c_2(w_2*f_2) + ...
就存在一个关于s的确定的解,

s_0 = c_0(k)

也就是说,如果不做一些诸如maximize的speculate的动作,而是遵循实际的技能水平的话.
那么是有一个确定可预知的结果的.

这个解的约束是\sum c_i(w_i,f_i) = 0.

同样的,如果从general的角度来看的话,因为无法在事前确定c(w,f)的具体形式和描述.
所以事前并不能根据实际的描述,来通过调整相应的f取得满足上面约束的解.

但同样的考虑一个特殊形式.
即所有的c(w,f) = 0的情况.

这是对于上面约束的一个特殊解.

即,如果把能够影响结果的因素的贡献都降低到0.
那么也就相当于实际上把这个影响因素移出等式/排除掉了.

世俗上来说,就是如果能把"紧张"之类的情绪等因素排除掉干扰,那么就存在对结果的一种确定性的预期.

代价则是使得结果不存在变数,也就是不存在着某种程度的投机/优化.

所谓的明镜止水.



2015-12-13

同一类人

下午看 万万想不到 的时候在想一个问题.
好像很长一段时间以来的笑点都是在一些对所谓梗的理解上.

有时候只是意识到了用某些元素,但镜头还没达到就先笑了.

而有些时候,对于一些努力制造的笑点可能却没什么反应.
即使周围的人都笑了.

这里不是想说笑点高低的问题.
而是似乎很多行为的动机只是在寻找某些熟悉的东西.

包括一些社交网络上的一些动作.
很多时候也就是一些基础元素的延伸反复变化而已.

就像蛤一样.

有些内容其实并没有什么太大的意义.
但是通过某种联想和映射使之跟某些旧有的东西产生共鸣的话,仿佛就会产生一些奇怪的作用.

比如前段时间12306的验证码.

所谓的一个脑洞,就对各个小圈子/领域的人产生了某种群体创作和参与的潮流.

如果去考虑为什么有这种流向度的话.
一方面可能是某种讽刺形式扩散.

但可能更主要的是形式上的可重复性的某种reinforcement.

无论是哪个圈子的验证码组合,其内容也并没有带来什么新的东西.
反而可能是一些该圈子所共知的一个基本常识/事实而已.

一个更通常一些的形式可能就是某些特定的回复模式,或者说管有的吐槽方式.

就像某些账号黑穆斯林产生的关于羊的回复.

或者更常规的,从 跪了 到 这是我的膝盖 之类的同义固形的回复句式的应用.

再典型一点的就是某些所谓的表情所构成的聊天记录.

尤其是表情.

考虑聊天的内容构成是存在一定的上下文连续性的.
而纯表情能构建起来并继续下去,从另一方面看就是说明这些表情构成了一定的内容描述.

换个角度,如果把"表情"考虑为某种语言的基本"单词"呢.

或者更广泛地来说.

在对外部交互的时候,选择一些固有的模式作为回应的行为.
某种形式上就是以这些"旧"的东西作为基本的语言单位,重新"遣词造句"对外部做出反应的一种类语言行为

于是这种某种圈子内的reinforcement的行为,以及某些笑点上的行为实际上就可以考虑为是基于某种"语言"的共鸣交流/反应.

某种程度上来说,就是某种特定文化背景下面的语言交流方式.
即"共同语言".

所以,实际上,所谓的"共同语言"就是对某些事物的基本的共通性的认识.
或者说带有类似的上下文的演绎方式.

在既定的context下面,通过提供有限的几个元素/"基本词汇"的话,双方就存在着一种限定范围内的,对同一事物进行相同或者类似演绎的可能.
也就是通常所说的"一致的思路"或者"相似的想法",从而完成所谓的"交流".

那么考虑,把"梗"作为基本的"词"考虑.
把表述形式不同,但同义的"梗"考虑为同义词的话.

重新审视各个圈子/领域/亚文化.

比如,
"回老家结婚"

"已经没有什么好怕的了"

再比如
"PHP是最好的语言"

又比如
"谈笑风生"
"续一秒"
等.

看到的时候可能很快地会有类似的短句或者下一句/下几句的内容.

有时候,与其说这是一种交流,不如说是一种补完.
或者说同义反复.

它并不是在讨论什么具体的内容.
而是只对一个固有的模式/模型进行填充.

就好像人听一些动物比如猫的叫声一样.

可能它确实表达了某种信息.

但是表达的形式却不一定具有固定的形式.
而只是某种模式上的相似感/区别度.

人的语气变化也类似.

不同的语气或者同一语气的不同的句子可能都是某种情绪/内容的表达.
而决定其性质的可能并不是"句子"的词汇构成.

所以,某种程度上来说,"交流"只是一种把自己"所想"/"意愿"通过某种模式replicate出去的方式/方法而已.
或者说是context的传递.

考虑眼神交流或者任何形式的交流.

把context以信息量计量量化的话,把交流时间考虑进来作为效率衡量维度.

"只要你一个眼神肯定"
这种描述的其实就是双方拥有类似context的情况下,传输过程可以以一种optimized的"压缩编码"方式进行.
从而缩短交流时间.

反过来说,沟通效率的高低就在于双方context的对等/交集情况如何.

所谓的同一类人的定义.


2015-12-08

由拉格朗日乘数想到

考虑lagrange multiplier的构造.
\mathcal{L}(X) = f(X) + \lambda*g(X)

这里一个比较有趣的地方是constraint function g作为一个余项引入.

根据g的定义,g(X) = 0.
也就是说实际上等价于
\mathcal{L}(X) = f(X)

考虑\mathcal{L}(X)的stationary points.
即满足\nabla \mathcal{L}(X) = 0的集合S.

如果s \in S且g(s) = 0的话,那么就是一个可能的solution.

这里的思路其实是通过构造一个零项保证function的mapping空间/关系不变.
但同时把问题简化为简单的stationary的解.

换个角度考虑的话,其实就是对一个现有space的某种形式的cut/prune/classify.
在保证某种特性不变的前提下,以g做出一定形式的划分.

考虑把f(X)看作是一种feature -> object的函数.
比如X是一组feature,object是一个用户标识.

也就是说假设存在一种描述,使得这个描述对某个用户唯一.
即通过feature vector可以unique地locate到某个用户.

然后考虑有一个对用户的label函数.
使得每个用户对应一个label.

重新如lagrange multiplier的形式的等式.
\mathcal{L}(X) = f(X) + \lambda_a*g_a(X) + \lambda_b*g_b(X) + ...

当X被归类为a的时候,其余的\lambda*g(X) = 0.
即此时的\mathcal{L}(X) - f(X) = \lambda_a*g_a(X) != 0.

如果令lambda都等于1的话,则形式为:
\mathcal{L}(X) = f(X) + g_a(X) + g_b(X) + ...
->
\mathcal{L}(X) = f(X) + col_sum(G*X).
G为一个dim(label)行feature列的matrix,col_sum为对列做的算数和.

那么就有可能解出一个matrix G,使得col_sum(G*X) = 0

对于一个给定的x,apply之后有col_sum(x;G) == 0 意味着什么呢?

意味如果x是在X中,且其对于label a的话,
\lambda_a*g_a(X) + \lambda_b*g_b(X) + ... == 0
->
\lambda_a*g_a(X) == 0即与label a的前提矛盾.

所以如果col_sum(x;G) == 0 的话,则表明x必定不在X中.

那么如果col_sum(x;G) != 0的话呢?

明显也不能说明x就在X中.
因为可能存在g_b(X) != 0等的情况.

如果G*X得到的vector形式V是[1,0,0,0,...]的某个维度值为1的unit vector呢?

也不能.

因为如果把G看作是X向feature space的project的话.
对于某个vector来说,project成unit vector也不足以说明就是在原来的X space.

所以,某种程度上来说,这个只是一个dim(X) -> dim(label)的某种形式的类bloom filter.

即G*X != unit vector的话,则必然不在原来的X中.











2015-12-06

授人以渔

早上看到条关于微博信息流展示算法调整的解释文章.

大致看了下,基本的思路是通过减少汇入的信息条数达到提高阅读率和互动率.
采用的手段里有个比较矛盾的是通过"时移",也就是把之前时段的内容,通过算法把评断为"用户可能在意而又错过"的"过时"微博重新编入信息流.

后面这个做法大概是为了后来人为操控某些信息,比如广告推广资源留的吧.
不过思路也不算完全自私.
毕竟,也算某种程度上的消息填谷去峰.

但是作为效果衡量的KPI选用阅读率和互动率可能就有失偏颇了.

考虑到阅读率/互动率其实是 交互数/信息流微博 数算出来的.
而调整的基本思路和策略则是减少汇入时间线的微博数.
也就是说,相当于直接调整/缩小基数.
于是,阅读率/互动率这几个KPI出现明显增长也算是自然甚至说理所当然的结果了.

所以,某种程度上来说,以这两个指标来衡量主时间线/信息流质量的话,难免缺乏些说服力.
而且,从比较量化的角度来说,要把这两者作为衡量指标的话,也还得除去基数变化带来的对结果的影响.
即,要以这个指数来证明调整确实有好的作用的话,也得把由基数变化带来的变化的影响部分去掉.

因此,至少从数据上来说,这个调整就算有效果,那么效果也不是里面提到的数字.
而是应该更低些.

那么,应该如何才能比较合适地评定一个信息流的好坏呢?

广义的高互动的内容并不一定就是用户所喜欢或者希望接受的内容.

一个例子就是最近那个猎鸟的事件.
自己的时间线上相关的内容其实不少,但考虑到对国内媒体的节操和舆论作风来说,其实没有什么悬念和值得关注的点了.
但是如果单纯时间线上其他人的互动角度来说,机械的算法可能就会认为这个可能感兴趣的内容.

出现这种bad case的原因在于,对用户的"好的内容"的定义过于局限,或者说维度过于片面.
个人的决策过程中还包含一些认知上的倾向性context.
尤其是对于,"好"/"坏"这种主观意愿性很强的东西.

即使是人本身也很难对喜好做一个准确的划分和定义.
毕竟,这是一个很动态的东西.

客观上来说,所能观测到的只是一个人对某条或者某些微博是否有交互的行为.
而这个交互行为本身是积极的还是消极的信息接受过程是未定的.

就像个转发/评论一条微博并不一定是赞同,也可能是批驳.

即使是 赞 这种设计上是积极倾向的动作,有时候也不一定就是赞同的意思.
比如赞的内容是一条转发微博,而里面可能是夹杂了两个对立观点的.

所以,有时候能做的,更多的只是一种交互意愿性的描述.

而要描述交互意愿,可用的维度就多很多了.

比如点开一个微博的转发/评论列表.
点开某条微博的昵称/头像.
点击了某条链接.
阅读长微博时滚动条的变化速率等.

基本上,所有的交互动作都是有倾向性并且可记录的.

那么,对于个人来说,有强交互意愿的内容,对于其他人来说,就也一定存在强交互意愿么?
或者进一步说,交互的概率就比其他普通信息更高么?

这个可能比较难量化.

毕竟,前面说了,这是个很主观的东西.

对于信息的接受方来说,推送过来的可能各个层面和维度的其他人的强交互意愿内容.
但可能只会对其中的某一些部分有较高的交互概率.
而如何界定这一部分的特征,就涉及到当时的立场/认知甚至于心情了.

所以,原则上来说,干涉调整个人的信息流的内容,可能是一个有作用但未必有效的方式.

实际上,早前Google+的circle/分组/list就有一个调整该circle/分组/list内的人的信息在主信息流出现频率的功能.
但基本上最后就只是看和不看的区别.
而不是看多少的区别.

毕竟分组的存在本身就已经是个人调整的结果了.

有特别想关注的自然会做一些分组方便浏览.

如果一个人的内容多数时候不感兴趣或者反感,就直接取消关注就是了.

某种程度上来说,主时间线信息流只是一个相对来说辅助性的东西.
虽然从使用角度上来说,这个是主要场景.

但如果作为一个信息消费品来说,主信息留只是一个广场集市入口.
它要保证的只是内容和来源的多样性.

立场和观点多了,自然能从转发链中吸收到新的关注点/人.


2015-12-04

反编译的一点想法

最近两天在看某券商的Android App.

当然,说看其实不太确切.
只是dex2jar然后再jd回来的混淆代码.

大概是连猜带蒙的看吧.

看的时候发现一些disassemble失败的部分,直接给的是bytecode.
还有一些是比较奇怪的代码分支层次.

前者倒是没想出什么原因.
而且也大致不算影响阅读,毕竟java bytecode其实还是比较简单可读的.

后者的原因想了想,大概是vm结构的区别了.
毕竟,dalvik是基于register,而jvm是stack base的.

register base的话,因为数量限制,加上一些优化策略,涉及到register的复用.
所以在跟源代码对译的时候,难免会出现不一致的地方.

尤其对于Java这种强类型语言来说.
ClassA和ClassB因为"生命周期"的不同可能导致复用同一个register,导致还原的时候,代码效果不是很理想.

而stack base的原则上来说就没有这个问题.
有多少个源代码级别的local variable就有多少个对应的bytecode里的variable.
所以对译起来相对简单得多.

想了下,自己实现JVM bytecode disassembler的话,好像不难.
变量,分支,签名,异常,类型都有.
大概是时间和精力/兴趣问题了.

然后想了下dalvik的情况.

如果搞定变量对应/区分的话,其他应该跟JVM的难度差不多.

考虑,在从source code到bytecode的translate过程中,对register的allocation策略.

一个naive的方式自然是算下source code级别的变量的作用域/生命周期了.
如果到某个点不再被使用,那么其所占用的register自然可以被复用.

某种程度上来说,这是个data dependency的问题.
那么大致的算法过程应该是以数据为中心展开的.

也就是基本的读写操作的序列化和拆分.

换个角度来说,就是以数据/变量为基本symbol的state machine/DAG了.

如果把某个变量v考虑为一个基本basic symbol或者symbol class.
一个赋值或者自增等操作之类的读写之后衍生一个同一symbol class的derivative symbol.
两个symbol之间做个link或者transfer标记.

那么数据之间如果有依赖的话,就是不同的symbol class之间存在link.

于是,一趟parse下来就得到一个大致的forest like的graph了.

这个graph的用处理论上来说可以做一定程度的并行编译.
如果不考虑实际的物理计算代价的话.

毕竟,每个不同symbol class的link intersect point就是实际的synchronize point.
在此之前的都算是独立数据流,可以分配到不同的CPU上执行.

当然,实际代价是另一回事.
而且真考虑的话,优化过程加上一下代价预估,也是顺手的事情.

另一个用途自然是register的allocation了.

如果不考虑dead code elimination的话,那么所有数据流应该都是会汇集到同一个点的.
不管这个点是真实的数据点(return value),还是一个pseudo/dummy.

那么,从这个角度看的话,其实这个graph就退化成tree了.

于是,重新考虑register allocation的策略.

对于基本的单层或者说双层(包含root)的tree来说,所需要的register数量就是root的children的数量.
递归推广下,对于多层的结构来说,就是整个tree structure里面,需要的register的数量就是至少是某个node的直接children的最大值local_max_children.

但实际所需的最大register数并不一定就是这个local_max_children.
因为当把这个subtree eliminate之后,实际上可用的register数为local_max_children-1,即reduce之后的结果要占一个register.

那么怎么算具体需要多少个register呢?

对于一个给定的tree,eliminate的策略应该是首先找leaf node上一层里children数最多的做prune.
因为这时候只能prune这一层的node,且这时所需的register数必然是最大children数.

于是,递归一下,reduce回root的话,实际上就算出了实际需要的register数量了.
而且,如果不关心具体数量的话,那么递归的过程实际上也就是allocation/reuse的过程了.

这个是source code到bytecode的translate过程.

但问题是如何逆向回来.

考虑到正向的过程其实类似某种变种形式的后续遍历形式.

如果正向过程中,对register的选择有固定的顺序或者选择策略的话,那么直觉上这个过程应该是可逆的.

因为每消耗一个register代表的其实是一层max children的leaf tree的elimination.
而每一个被小号的register的读就意味着一次subtree的merge/面向root的propagate.

所以理论上应该是可行的.




聊聊卡布里尼

最近看了部片叫卡布里尼,算是可能这段时间来比较有意思的一部电影. 故事也不算复杂,就是一个意大利修女去美国传教,建立慈善性质医院的故事. 某种程度上来说,也很一般的西方普世价值主旋律. 但是如果换一套叙事手法,比如共产国际的社会主义革命建立无产阶级广厦千万间的角度来看的话,也不是...