Month: 十一月 2017

bug 错题本

发现有一些错误是自己经常犯的,不记录下来回顾一下,总是会一直犯。高中的时候还有习惯来记录错题,然而这么好的习惯,到了大学里却莫名其妙的没有了,知道毕业两年后的今天才突然想起。所以第一个场贩的错误就是思想上的——学习是一件终身的事,要延续好习惯。

对于各种持有资源的连接池对象等,要时刻注意它们是否是线程安全的。

Python

更新变量的value的时候,变量名写错了,导致创建了一个没有用的新变量,老变量的value却没有更新

解决:

  1. 使用pylint 检查代码,会提示 unused variable;
  2. 在写代码的时候多用自动补全,而不要手动打出每一个变量。

Python 的变量声明和赋值没有分开,这个是一个坑,只能通过多用工具检查来实现

故障排查

  • 有时候删文件删不掉但是空间还在占用是因为还有程序在使用这个文件

  • CPU wait 比较高说明在做io

  • 操作系统相关问题(OOM)

    dmesg -Ttail -200 /var/log/kern.log 查看系统kill进程的日志,可以看到被kill掉时候的内存占用,再用free -m 查看一下是否超出了内存

使用 ipdb 单步调试 Python 代码

pdb 是 Python 标准库中自带的 debugger,ipdb 是基于 ipython 的增强版 pdb。

常用命令

  • [n]ext 下一步
  • [s]tep into 进入函数
  • [r]eturn 跳出函数
  • [b]reakpoint 打断点

pdb

pdb is fine, just don’t have so many features

Usage: import pdb; pdb.set_trace()

Ipdb is better

Usage: import ipdb; ipdb.set_trace()
n next
p print
pp pprint
s setp into
c continue to next breakpoint
b breakpoint
a args

sklearn 入门笔记

前一阵看了一个叫做 [莫烦 Python][1] 的教程,还有 [sklearn 的官方教程][2] 初步了解了一下 sklearn 的基本概念,不过教程毕竟有些啰嗦,还是自己记录一下关键要点备忘。

机器学习要解决的问题

什么是机器学习?

sklearn 给了一个定义

In general, a learning problem considers a set of n samples of data and then tries to predict properties of unknown data. If each sample is more than a single number and, for instance, a multi-dimensional entry (aka multivariate data), it is said to have several attributes or features.

翻译过来:

总的来说,“学习问题”通过研究一组 n 个样本的数据来预测未知数据的属性。比如说,如果每个样本都不止包含一个数字,而是多维的向量,那么就称它为有多个 feature 属性。

要解决的问题

机器学习的方法不外乎这几类,现在自己用到的应该是分类比较多。

  1. Classification 分类,也就是离散的
  2. Regression 回归,也就是连续的
  3. Clustering 聚类
  4. Dimensionality reduction 数据降维

要实现上面几个目标,可能需要下面的步骤

  1. Model Selection 模型选择
  2. Preprocessing 数据预处理

要去判定自己的任务需要用哪种方法,优先参考 sklearn 官方推出的 cheatsheet(小抄)

sklearn-leanr cheatsheet

sklearn 的数据库

sklearn 为了方便学习自带了一些数据库,可以说是非常方便了。包括了 iris 花瓣数据库,手写数字数据库等。这些例子可以说相当于编程语言的 hello world 或者是图形学届的 utah teapot 了。

除了真实的数据集,还可以使用datasets.make_*系列函数来直接生成一些数据集用来测试。

>>> from sklearn import datasets
>>> iris = datasets.load_iris()          # iris 花瓣数据库
>>> digits = datasets.load_digits()      # 手写数字数据库
>>> print(digits.data)                   # 数据库的输入
[[  0.   0.   5. ...,   0.   0.   0.]
 [  0.   0.   0. ...,  10.   0.   0.]
 [  0.   0.   0. ...,  16.   9.   0.]
 ...,
 [  0.   0.   1. ...,   6.   0.   0.]
 [  0.   0.   2. ...,  12.   0.   0.]
 [  0.   0.  10. ...,  12.   1.   0.]]
>>> digits.target                        # 数据库的输出
array([0, 1, 2, ..., 8, 9, 8])

其中 data 属性是一个二维数组,格式是(n_samples, n_features).

关于如何载入外部数据库,可以看这里,实际上我也还没看,科科

学习与预测

以识别手写数字为例,我们要做的是根据图像识别出数字是什么来。我们需要 fit (训练)出来一个 estimator,然后用来 predict (预测)未知数据的类型。在 sklearn 中,一个 estimator 就是一个实现了 fitpredict 方法的 object。estimator 常用的属性还有 get_params, score 等。

比如我们使用支持向量机模型:

>>> from sklearn import svm
>>> classifier = svm.SVC(gamma=0.001, C=100.)

>>> classifier.fit(digits.data[:-1], digits.target[:-1])  # 注意第一个参数是数据,第二个参数是结果
SVC(C=100.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape="ovr", degree=3, gamma=0.001, kernel="rbf",
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False)

# 现在我们开始预测最后一个数据
>>> classifier.predict(digits.data[-1:])
array([8])  # 得出的结果是 8

实际上的图像是

last digit

刚刚的例子是使用前面的数据做训练,然后识别了最后一个数字,其实我们还可以使用 sklearn 自带的 train_test_split 函数来分割数据

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    iris_X, iris_y, test_size=0.3)

# 注意其中训练数据被 sklearn 打乱了。在机器学习中,数据比较乱是比较好的,算法其实也一样,数组是乱的最好。

完整的例子在这里:http://scikit-learn.org/stable/autoexamples/classification/plotdigits_classification.html

保存模型

训练模型还是很花费时间的,我们不可能每次都去训练一个模型,所以一般都是离线训练好了之后,保存下模型来,然后在线调用。保存模型可是直接使用 Python 内置的 pickle 模块,但是一般模型数据都比较大,pickle 对大文件支持不好,最好采用 sklearn 自带的 joblib.

>>> from sklearn.externals import joblib
>>> joblib.dump(classifier, "filename.pkl") 

>>> clf = joblib.load("filename.pkl") 

很简单吧

其他的一些技巧

一些约定

上面说到 sklearn 约定了 fit 和 predict 方法,还有一些其他的约定

  1. 所有的输入都会被转化为 float64 类型
  2. 一半习惯用 X 表示样本数据,y 表示预测结果

可视化

>>> X, y = datasets.make_regression(n_samples=100, n_features=1, n_targets=1, noise=10)
>>> plt.scatter(X, y)
>>> plt.show()

会有下面的图

参考

  1. https://sklearn.apachecn.org/docs/0.21.3/2.html
  2. https://morvanzhou.github.io/tutorials/machine-learning/sklearn/1-1-why/
  3. http://scikit-learn.org/stable/tutorial/basic/tutorial.html

安卓开发中的Context

在安卓当中,Context几乎是无处不在的,每一个Activity是一个Context,每一个Service也是一个Context。

但是如果你新起了一个线程的话,你需要显式地把Context传递进去。

比如下面的例子:

public class DumpLocationLog extends Thread {
    LocationManager lm;
    LocationHelper loc;
    public DumpLocationLog(Context context) {
        loc = new LocationHelper();
        lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    }
    public void run() {
        lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000L, 500.0f, loc);
    }
}

然后使用这个线程的时候,把this,也就是一个context的实例传递进去

new DumpLocationLog(this);

if you are in a fragment, use getAcitvity()

if you are in an anoynmous onclicklistener, this is MainActivity.this

为安卓编译64位的dropbear

如何使用dropbear

这里主要是需要在安卓上生成 host key,以及把自己的公钥传到安卓上

dropbearkey -t rsa -f /data/local/dropbear_host_key # 在安卓上生成key
adb push ~/.ssh/id_rsa.pub /data/local/authorized_keys # 在宿主机把自己的密钥传过去
dropbear -F -E -r /data/local/dropbear_host_key -A -N root -C jk -R /data/local/authorized_keys # 按照给定的key启动dropbear
dropbear -P /data/local/dropbear.pid -r /data/local/dropbear_host_key -A -N root -C jk -R /data/local/authorized_keys # 以daemon形式启动dropbear

如何为64位的安卓机器编译 dropbear

需要更改如下代码(svr-chansession.c):

addnewvar("LD_LIBRARY_PATH", "/system/lib");

to:

addnewvar("LD_LIBRARY_PATH", "/system/lib64");

使用AIL把dropbear添加为服务

service sshd /system/xbin/dropbear -s
   user  root
   group root
   oneshot

试过了,但是没有成功

如何重启adb(wifi)

setprop service.adb.tcp.port 5555
stop adbd
start adbd

关闭 ssh key 验证

Host *
    StrictHostKeyChecking no

mount -o remount,rw /system

参考

  1. http://forum.xda-developers.com/nexus-7-2013/general/guide-compiling-dropbear-2016-73-t3351671
  2. http://forum.xda-developers.com/nexus-7-2013/general/guide-compiling-dropbear-2015-67-t3142412/page3

安卓的 AsyncTask

asynchronusally run task without explicitly creating thread.

Usage

doInBackground(Params...)
onProgressUpdate(Progress...)
onPostExecute(Result)

Here is an example of subclassing:

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }

    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}

 
Once created, a task is executed very simply:

new DownloadFilesTask().execute(url1, url2, url3);

template parameters can be Void, Void, Void

see https://developer.android.com/reference/android/os/AsyncTask.html

Linux 内存与进程管理(ps/top/kill…)

ps 命令

比较有用的选项有

-e 显示所有的进程
-f 显示 uid, pid, ppid, cpu, starttime, tty, cpu usage, command
-j 显示 user, pid, ppid, pgid, sess, jobc, state, tt, time, command。个人更喜欢 -j 一点
-l 显示 uid, pid, ppid, flags, cpu, pri, nice, vsz=SZ, rss, wchan, state=S, paddr=ADDR, tty, time, and command=CMD.
-v 显示 pid, state, time, sl, re, pagein, vsz, rss, lim, tsiz, %cpu, %mem, and command

-L 显示能够排序的关键字(mac)
L 显示能排序的关键字(Linux)
-o/-O 指定显示的列,-o 只显示指定的列,-O 有默认的几列,等价于:-o pid,format,state,tname,time,command

-S 把所有已经退出的进程的时间计算到父进程上(mac)
S 把所有已经退出的进程的时间计算到父进程上(Linux)
-u 按照 uid/username 过滤
-p/--pid 限制 pid
-g/--gid 限制 gid
-C 按照命令过滤
--ppid 按照 ppid 过滤
--ssid 按照 ssid 过滤
--tid 按照 tty 过滤

-E 显示环境变量
-H/--forest 按照进程树显示

--sort 按照某一列排序

-ww 不要限制显示的输出宽度

例子

  1. 使用 ps 显示占用内存最多的进程
% ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | head
  1. 显示所有进程树
% ps -ejH

其他相关命令

pstree 查看进程树

pgrep process_name 按照名字查找进程 pid

top 命令

定时刷新系统的进程状态的监控程序。mac 上的默认排序是 pid,Linux 上是 %CPU。mac 上的 top 程序和 Linux 上非常不一样。

  • top 教程:https://linoxide.com/linux-command/linux-top-command-examples-screenshots/
  • htop 教程:http://www.cnblogs.com/lizhenghn/p/3728610.html
  • atop 教程:http://www.cnblogs.com/bangerlee/archive/2011/12/23/2294090.html

atop

atop 用来分析机器在历史上的负载情况。通过 crontab 固定时间采样,组合起来形成一个 atop 日志文件,可以使用 atop -r XXX 对日志文件查看。

atop 每天以一个 /var/log/atop/atop_YYYYMMDD 的形式生成一个日志文件。

常用命令

  • b mm:ss 到指定时间
  • t 查看后十秒
  • T 查看前十秒
  • m 按照内存排序
  • C 按照 CPU 排序
  • c 查看详细命令

kill 命令系列

kill -s SIGNAL pid

或者 pkill xxx,不要使用 killall ,use pkill over killall

调整 nice 值

使用 nice 和 renice。nice 的范围是 -20 ~ 19,nice 值越低,优先级越高。

nice -n 10 COMMAND # 以 10 为初始 nice 值启动命令
renice 10 -p pid

jobs 命令相关

jobs list all jobs

command & put job in the background

fg N to put it in the foreground

bg N to put it in the background

ctrl-z to put a job to sleep

kill %n to kill job number n

lsof

lsof -i:port 列出指定端口对应的进程

lsof -u username 指定用户

lsof -c process_name 指定进程名

lsof -p pid 指定 pid

使用 dmesg 查看当前的内核日志,debian 上可以查看 /var/log/kern.log /var/log/dmesg.0

RSS is Resident Set Size (physically resident memory – this is currently occupying space in the machine’s physical memory), and VSZ is Virtual Memory Size (address space allocated – this has addresses allocated in the process’s memory map, but there isn’t necessarily any actual memory behind it all right now).[1]

[1] https://stackoverflow.com/questions/7880784/what-is-rss-and-vsz-in-linux-memory-management

分布式系统中的锁

分布式系统需要使用分布锁。首先我们来回忆一下在单机情况下的锁。

当我们的程序在需要访问临界区的时候,我们可以加一个锁,如果是多线程程序,可以使用线程锁,如果是多进程程序,可以使用进程级别的锁。但是在分布式的环境中,如果在不同主机上部署的程序要访问同一个临界区是该怎么做呢?这时候我们需要分布式的锁。

当部署的服务或者脚本不在同一台机器上时,使用分布式的锁,可以使用 zookeeper 或者 redis 实现一个分布式锁。这里主要介绍一下基于 redis 的分布式锁。

redis 官方给出的单机 redis 分布式锁:

加锁

NX 命令指定了只有在不存在的时候才会创建,如果已经存在,则会返回失败。EX 指定了过期时间,避免进程挂掉后死锁。值设定为了一个随机数,这样只有加锁的进程才知道锁的值是多少

SET resource-name my_random_string NX EX max-lock-time

解锁

因为解锁时会检查是否提供了随机数的值,所以只有创建锁的进程才能够解锁。

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end
EVAL "script..." 1 resource-name my_random_string

参考:

[1] https://redis.io/topics/distlock