Category Archives: Tech

一辈子也离不开的互联网与软件技术。

人工智能20年

20年前,我听到一条新闻,IBM的“深蓝”电脑战胜了人类的世界象棋冠军卡斯帕罗夫。于是,对新潮事物未知欲旺盛的我,买了这本书。

我清楚记得20年前那个论断:

是的,虽然现在电脑战胜了人脑,但仅限于国际象棋领域,在围棋领域,电脑是永远不可能战胜人类的,因为围棋的可能性太多了,甚至超过了宇宙的原子。

现在你会不会觉得这个论断很可笑?

那就是20年前的认知。

最近读罢开复老师和吴军老师的两本书,对人工智能有一个新的认识。(更推荐吴军老师的《智能时代》)

学习记录如下(完全个人理解,字眼上不一定完全准确):

  1. 1个世纪前,机械思维曾经是人类的骄傲,就像今天的人工智能。
  2. 人类曾经认为机器在围棋上无法战胜人类,是因为人类当时依然在用机械思维来理解这个问题。计算机事实上不需要计算每一步棋往后推导多少步的各种可能性(象棋当年是用很强的计算力来做到的),他只需要“学会”计算每一种走法和最后“胜利”这个结果的关联性。
  3. 人工智能的本质,其实就是数据与结果之间的相关关联关系的计算,机器学习即是一种人类把数据与结果都告诉机器,让机器自己进行学习,从而获得从新的数据推导出新的结果的能力和算法。
  4. 人工智能的研究的一个突破就是大概在2011年发明的“深度学习”框架,其中神经网络算法影响力较大。
  5. 大数据其实就等于人工智能的“燃料”,因此两者经常放在一起谈。
  6. 机器学习的强项,就是在大量的数据中,找到一般人类无法用逻辑去理解的关联性。比如:疾病与大量指标之间的关联关系、金融市场上各种看似无关痛痒的信号和后来的发展。
  7. 人工智能最先最有价值应用的几个领域可能是:医疗、法律、广告营销、安保、翻译等。
  8. 机器永远服务于人类,人类不必要对人工智能的发展过份担心,事实上科幻电影和大家的预言总是会比现实更快更乐观。20年过去了,其实我并没有觉得生活有太大的改变,只是更方便了,更透明了,信息不对称更少了,世界在往更好的方向发展。

手波与自动波:框架程序员与程序员

最近技术团队扩张,很多有丰富经验、来自不同地方的程序员加入了我们团队。于是我们开始切磋,交流不同的框架和设计模式,脑洞大开之余,让我感受到使用不同的方式的程序员,就有如开车开手波和自动波的区别。有可能,这个区别还会更大。

相比起Java的框架会被SSH一统天下,PHP程序界可谓杂草丛生,10个人大概会用8个框架。而这些框架,有一部分都深怀“远大”的理想,要把所有事情一所包办,写下一大堆的约定,功能也越来越多,到最后越写越重,学习成本相当高。

如果一个程序员学习框架都使用了超过1年,对某个框架相当的熟练,但当事过境迁,一个系统使用3年都终将重构,新的技术和新的框架崛起之时,这些框架程序员是否发现自己仍像初哥般无能为力?

归根结底,授人以鱼不如授人以渔,告诉你怎么实现,而不去思考为什么这样实现,就会变成框架程序师。Jeff Bezos也曾经说过,我永远只喜欢思考永恒不变的东西,这就是方法论。

因此,我们只鼓励最简单的框架,而任何人需要从零学起,从零写起,并且知道每一个部件的原理和实现,你不单止需要知道用框架怎么做,更重要的是知道如何不用框架怎么做。

有一些共性的东西,你甚至可以抛开语言和框架甚至技术的层面。下面是我16年编程经验总结出来的一些方法论:

软件工程的几个重要原则:

  1. 单元代码块尽量简短。通常的约定是:一个类不超过500行,一个方法不超过30行。

  2. 单元代码块职责单一。一个方法只做一件事情。

  3. 对接口编程,而不是对实现编程。

  4. 设计模式的核心思想:解耦。只有解耦,才能让每一块代码更通用,可重用,及可维护。

  5. 任何情况下,代码编写的高可读性原则都应该放在第一位,你要保持你代码逻辑的足够简单,用最直观明白的命名而非抽象晦涩。代码的可读性都是为了高可维护性及适应快速迭代,因此,软件工程确实就是工程,写软件就像盖楼,根基或架构不牢终将引发倒塌。

今天终于发布了自己在composer的第一个包

虽然是迟了一些,但是总算是迈出了为人类软件事业贡献的第一步。

陆续会有更多的自己十年代码累积的包推出,敬请期待。每个包都是相当轻量级的,贯彻这十年来的软件开发宗旨:CoolLittleThings,Keep it Simple, Stuipid!

第一个包介绍:URL处理包,相当实用,但外面所见不多(都好像太重了)

https://packagist.org/packages/rayful/url

URL

一个对URL的通用操作类。

安装(Composer)

composer require rayful/url

基本用法:

$URL = new URL("http://abc.com/?page=1&param=a");
$URL->setQuery(["page"=>2]);
$newUrl = $URL->build();

echo $newUrl;   //will output: http://abc.com/?page=2&param=a

$URL = new URL("http://abc.com/?page=1&param=a");
$URL->replaceQuery(["page"=>2]);
$newUrl = $URL->build();

echo $newUrl;   //will output: http://abc.com/?page=2

可以全局地urlencode整条URL:

$URL = new URL("http://abc.com/目录1/目录2/index.php?cat=科技&rate= 非常好");
$URL->encode();
$newUrl = $URL->build();

echo $newUrl;   //http://abc.com/%E7_%AE%E5%BD_1/%E7_%AE%E5%BD_2/index.php?cat=%E7%A7_%E6__&rate=+%E9__%E5%B8%B8%E5%A5%BD

构造函数不传参,默认为改变当前URL(需要在浏览器内)的某个参数:

$URL = new URL();
$URL->setQuery(["page"=>2]);
$newUrl =  $URL->build();

快捷方式:

echo URL::append(['page'=>2]);

四个静态方法的调用:

$domain = URL::domain($url);    //url.com
$queryString = URL::queryString($url);  //query=1&query=2
$query = URL::query($url);  //['query'=>1,'query2'=>2]
$dir = URL::dir($url);  //http://url.com/doc

Elastic Search使用ids来排序

弄了半天,终于在全部环境下搞店。

网上有一个版本,但不能使用,有点过于复杂:

http://damienalexandre.fr/post/elasticsearch-ids-query-order

我自己的版本:(PHP)

$this->es_sort = [
    '_script'=>[
        "script"    =>  "ids.indexOf(doc['_uid'].value.replace('{$this->type}#',''));",
        "type"      =>  "number",
        "params"    => [
            "ids"   =>  $ids
        ],
        "order"     =>  $order
    ]
];

如果是纯Query的版本,我也放一个出来给大家方便测试吧:(请自行修改ID及type的值)

{
    "query": {
    "ids": {
        "values": [
            "55088c72662a07fd758b457a",
            "551fd6d6662a078ffe8b4578"
        ]
    }
  },
  "sort": {
    "_script": {
        "script": "return ids.indexOf(doc['_uid'].value.replace('product#',''));",
      "type": "number",
      "params": {
            "ids": [
                "55088c72662a07fd758b457a",
                "551fd6d6662a078ffe8b4578"
            ],
        "order": "sac"
      }
    }
  }
}

我测试的elastic版本分别有:1.1、1.4.1、1.5.3,都不一样。我大概总结一下。

1、网站这个获得当前文档的_id值的方法现在新的ES都不能用了。

id    = org.elasticsearch.index.mapper.Uid.idFromUid(doc['_uid'].value);

2、我用一个迂回的方法拿到这个_id,留意这句:

ids.indexOf(doc['_uid'].value.replace('{$this->type}#',''));

3、还有最要命的一个问题,一开头我怎么测试都不行,弄了大半天。后来很仔细看那一长段的exception信息,我怀疑是新版的ES安全性考虑,把默认的script语言groovy屏蔽了。见这页:

https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting.html#_groovy_sandboxing

4、1.5.3之后,以及1.4.8,都默认不再支持_groovy。怎么办?如果按照他文档建议的方式,通过一个文件来传递脚本,无疑太笨了。我研究了半天,发现可以在ES的配置文件里加这句:

script.groovy.sandbox.enabled: true

重启ES,搞定,可以用了。

5、你要说不安全嘛?当然,ES建议你可以使用express language,那个实在有点弱,好像不支持indexOf。我实在是一个很懒和不愿意把代码变长的程序员。

如果你有更好的方法,欢迎告诉我。

scp断点续传  

 scp一旦出错中断就只能重新开始,不过可以利用rsync实现scp的断点续传

1、在~/.bashrc中加入一个alias:

$vim ~/.bashrc

alias rscp=’rsync -v -P -e ssh’
2、重新载入.bashrc配置
# source ~/.bashrc

Mac OS X上如何实现到Linux主机的ssh免登陆

本文要讲的,就是如何简化从Mac登录Linux主机的操作步骤,提高效率。所谓的“免登陆”其实是不存在的,只是说,从验证密码的登录方式,改为 公私钥对的登录验证方式。使用后者的方式,每次会由ssh客户端自动发送验证信息,所以就免去了人工输入密码,看起来好像“免登陆”一样。

关于这么做的原理,大家可以自己去Google,关键词是“非对称加密”,“RSA算法”,“基于ssh密钥对的自动登录”,等等,我就不多解释了。我直接说操作步骤吧:

  1. 生成密钥对
  2. 用密码登录远程主机,将公钥拷贝过去
  3. done

怎么样,无敌简单吧?

生成密钥对

执行命令 -keygen -t rsa
执行结果如下:

charles@mac:~ > ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/charles/.ssh/id_rsa):
Created directory ‘/Users/charles/.ssh’.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/charles/.ssh/id_rsa.
Your public key has been saved in /Users/charles/.ssh/id_rsa.pub.
The key fingerprint is:
c8:4b:85:87:90:7c:1a:67:b6:71:f5:51:0c:9d:a2:89 charles@TCMBP.local
The key’s randomart image is:
+–[ RSA 2048]—-+
| … .. o=.. |
| +.*o. …+ |
| Bo+o. o.. |
| …+E o |
| + S |
| . . |
| . |
| |
| |
+—————–+

注意:提示enter passphrase的时候,不要输入,因为你本来就想少打一次密码的,这里如果设置了用密码保护私钥,那登录的时候还是要输密码,就白做了。

做完这个步骤后,cd ~/.ssh,你就可以看到你刚才生成的密钥对,id_rsa是私钥,id_rsa.pub是公钥。下一步,就是把公钥拷贝到目标主机上。

将公钥拷贝到目标主机

用ssh登录到目标主机,然后cd ~/.ssh目录,如果目录不存在,那么要自己创建mkdir -p ~/.ssh。你今后要用哪个帐户登录主机,就在哪个帐户的home目录下操作,如果要免登陆root,就要去/root下操作。使用~比较好,不用多想了。

有了.ssh目录后,进去,然后把id_rsa.pub传过去,可以用scp命令,这里要做的一个主要操作,就是将id_rsa.pub,的文件内 容,写到一个叫authorized_keys的文件中去,如果目标主机的相应用户名下已经有了.ssh目录和authorized_keys文件,那你 操作要小心一点,可能别人也做过免登陆的设置,这个时候你要小心不要把别人的设置给覆盖了。如果没有的话,就创建文件touch ~/.ssh/authorized_keys,然后执行cat id_rsa.pub >> authorized_keys,将你的公钥写入到authorized_keys中,公钥文件.pub里面只有一行信息,上面的命令相当于把那一行信息 追加到authorized_keys文件最后一行。

如果.ssh目录是你主机刚刚创建的,那么可能还需要改变一下这个目录的权限,将权限放低,chmod -R 0600 ~/.ssh,到此,所有设置就算做完了,你可以退出登录,在自己的主机上试一下了,现在再敲入ssh命令后,不用密码就可以登录主机了。

Apache防盗链的使用

方法一,直接在apache设置通过Referer判断是否允许访问。

SetEnvIfNoCase Referer "^(ssl|http|https)://(.+\.)?yourdomain\.com" local_ref=1
<filesmatch "\.(txt|doc|mp3|zip|rar|jpg|gif)">
  Order Allow,Deny
  Allow from env=local_ref
</filesmatch>

经测试,这段代码会有一点问题,在IOS上的Safari,播放html5的音频视频的时候,是不能正常播放的。我猜可能是,IOS上的Safari在播放HTML5媒体时,调用的是客户端的播放器,是会不发送Referer的。

加上这一段代码即可解决:

SetEnvIf Referer "^$" local_ref=1

当然,每一段代码都是双刃剑,这样设置允许了空来源后,用户就可以把要下载的文件地址复制后直接粘贴并转到这个地址就可以下载了。但懂这样做的用户毕竟少,还是杜绝了大部分用户去盗链下载我的MP3导致服务器负荷增高的可能性了

方法二,设置Rewrite(未经测试,转贴以备用)

RewriteEngine On
RewriteCond %{HTTP_REFERER} !^http://(.+\.)?yourdomain\.com/.*$ [NC]
RewriteRule .*\.(gif|jpg|swf)$ http://yourdomain.com/nolink.png [R,NC]