L.Dragon Blog

stupid student


  • 首页

  • 归档

  • 搜索

Leela-Zero 代码分析(autogtp)

发表于 2018-09-18 | 更新于 2018-12-10 | 分类于 Codes |

本文主要分析 Leela Zero (v0.15, #9e903ed) 自对弈部分的代码

自对弈(Self Play/autogtp)

Leela Zero 自对弈部分的代码在 autogtp 目录下。入口文件为 main.cpp,主要的逻辑在 Management.cpp 文件中。

Main

自动下棋的主要逻辑可以总结为如下流程图。

  • STEP 1:main 函数启动,首先检查执行程度的目录下是否有 networks 文件夹,没有则创建一个,networks 文件夹存储模型参数。 之后绑定各种信号量,主要是在程序退出时进行数据备份和清理,可以参阅代码,在此不再细讲。
  • STEP 2:main 函数调用 Management::giveAssignments 函数分配初始任务。 在 giveAssignments 中,首先执行 sendAllGames(此处省略解读),然后调用启动一个单独的线程执行 bash 命令,如下所示

    1
    ./leelaz --tune-only -w networks/dec5b9d88877fe876a308b42370335c8f5465235d0104c889bdda3b42993aa28 --gpu=0

    之后,创建工作线程。每个 Worker 线程将其产生的 resultReady 信号与 Management 中的 getResult 函数进行绑定。 绑定通过调用 QT 的 API 函数 QObject::connect 完成,对 QObject::connect 的解释可以参考[1]。

    • STEP 2.1 提交作业:在线程启动前,会先向每个 Worker 线程提交一个作业(Worker::order),这个作业调用 Management::getWork 获取, 具体过程参考 Job-Assignment;
    • STEP 2.2 启动线程:Worker 线程启动后,会先执行初始化时提交的作业, 这个作业执行完成后会由 Worker 主动发起一个 resultReady 信号给 Management。 根据之前的信号绑定关系,Management 接收到 resultReady 信号后会调用 Management::getResult 函数进行处理。 在 Management::getResult 函数中会再次调用 getWork 函数获取作业,并提交给响应的 Worker 进行处理。 线程反复执行提交对弈作业,直到用户程序主动停止主进程的执行。

Job Assignment

Autogtp 作业都是通过请求远程服务器获取对弈的模型参数和对弈作业的类型,完成对弈作业后将执行的数据提交到服务器端。 作业执行的流程可有如下顺序图表示

执行 Management::getWorker 后会经过如下步骤

  • STEP 1:调用 Management::getWorkInternal 函数,autogtp 调用命令行函数获取作业,其类似于执行如下命令

    1
    curl -s -J http://zero.sjeng.org/get-task/16

    可请求的作业有三种类型,均匀以 JSON 格式的数据返回,分别是:

    • selfplay:进行同一个模型的自对弈,即黑白棋采用的推理参数相同,其 JSON 格式的作业数据如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      {
      "cmd": "selfplay",
      "hash": "dec5b9d88877fe876a308b42370335c8f5465235d0104c889bdda3b42993aa28",
      "hash_gzip_hash": "0fff7d2ea27611eec2fbeeab103c95770742437bb9e6a0ccd0af15bc9d3e23d1",
      "minimum_autogtp_version": "16",
      "minimum_leelaz_version": "0.15",
      "options": {
      "noise": "true",
      "playouts": "0",
      "randomcnt": "999",
      "resignation_percent": "5",
      "visits": "1601"
      },
      "options_hash": "b37dca",
      "random_seed": "5266686459798233275",
      "required_client_version": "16"
      }
    • match:进行两个模型之间的对弈,可以比较两个模型之间的好坏,其 JSON 格式的作业数据如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      {
      "cmd" : "match",
      "white_hash" : "223737476718d58a4a5b0f317a1eeeb4b38f0c06af5ab65cb9d76d68d9abadb6",
      "black_hash" : "92c658d7325fe38f0c8adbbb1444ed17afd891b9f208003c272547a7bcb87909",
      "options_hash" : "c2e3"
      "required_client_version" : "5",
      "leelaz_version" : "0.9",
      "random_seed" : "1",
      "options" : {
      "playouts" : "1000",
      "resignation_percent" : "3",
      "noise" : "false",
      "randomcnt" : "0"
      }
      }
    • wait:等待,其 JSON 格式的作业数据如下

      1
      2
      3
      4
      {
      "cmd" : "wait",
      "minutes" : "5",
      }
  • STEP 2:执行自动对弈

    autogtp 针对每种作业都定义了不同的执行逻辑,

    • selfplay:被定义为 ProductionJob。启动一个 leelaz 进程,执行 genmove b 下黑棋,genmove w 下白起。 启动 leelaz 的命令如下

      1
      2
      3
      4
      ./leelaz -p 0 -v 1601 -r 0 -m 999 -t 1 -d -n --noponder \
      -s 4893736926019914355 \
      --gpu=4 -g -q \
      -w networks/dec5b9d88877fe876a308b42370335c8f5465235d0104c889bdda3b42993aa28

      参数解释如下

      • -p: Weaken engine by limiting the number of playouts. Requires –noponder.
      • -v: Weaken engine by limiting the number of visits.
      • -r: Resign when winrate is less than x%. -1 uses 10% but scales for handicap.
      • -m: Play more randomly the first x moves.
      • -t: threads
      • -d: Don’t use heuristics for smarter passing.
      • -n: Enable policy network randomization.
      • -noponder: Disable thinking on opponent’s time.
      • -s: Random number generation seed.
      • -gpu: ID of the OpenCL device(s) to use (disables autodetection).
      • -g: Enable GTP mode.
      • -q: Disable all diagnostic output.
      • -w: File with network weights.
    • match:被定义为 ValidationJob。启动两个 leelaz,每个 leelaz 分别载入不同的对弈参数。其对弈过程如下

      假设运行白棋的为 leelaz_w,运行黑棋的 leelaz_b

      1. 当 leelaz_w 调用 genmove w 执行了一步操作后,会输出该操作的步如 C14;
      2. 在 leelaz_b 中执行 play white C14,然后调用 genmove b 执行下一步操作,输出操作步如 E17;
      3. 在 leelaz_w 中执行 play black E17,然后调用 genmove w 执行下一步操作,输出操作步如 H18;
      4. 重复上述两个步骤,直到一方认输。
    • wait:被定义为 WaitJob。执行 sleep 操作。

  • STEP 3:导出数据;

  • STEP 4:发送 resultReady 信号,调用 Management::getResult 请求下一个作业,重复 STEP1 - STEP4 的操作。 在 getResult 函数执行过程中,会上传模型数据。

参考资料

  • QT 的信号与槽机制介绍

基于 Sabaki 搭建围棋 AI

发表于 2018-09-15 | 更新于 2018-12-10 | 分类于 Applications |

Sabaki + Leela-Zero + Leelasabaki

本文主要阐述在 Windows/Linux 环境下搭建围棋 AI 的过程。使用到的软件栈为:

  • Sabaki:一个跨平台的围棋(Go)棋盘和 SGF 编辑工具。
  • LeelaSabaki:Leela (Zero) integration with Sabaki.
  • Leela-Zero:一个 AlphaGo Zero 论文 的开源实现

Based on Windows (Play for fun)

下载如下软件的 Windows 版本和相关权值文件:

  • Sabaki Release
  • LeelaSabaki Release
  • Leela-Zero CPU Only
  • Leela-Zero 权值文件

执行如下安装过程

  1. 安装 Sabaki 到路径 C:\MyPrograms\Sabaki【注意:安装路径避免空格】;
  2. 安装 LeelaSabaki 到路径 C:\MyPrograms\leesabaki;
  3. 安装 Leela-Zero CPU Only 到路径 C:\MyPrograms\leela-zero-0.15-cpuonly-win64;
  4. 解压权值文件到 C:\MyPrograms\leeazero。

启动 Sabaki,配置 Engines->Manage Engines,如下图所示

配置 Engine 参数为 --flat C:\MyPrograms\leela-zero-0.15-cpuonly-win64\leelaz.exe -t 6 -w C:\MyPrograms\leeazero\leelaz-model-swa-12-96000_quantized.txt --noponder,其中

  • -t:表示使用CPU的6线程;
  • -w:表示调用权重文件;
  • --noponder:表示对手时间内不思考;

然后,执行 Engines -> Attach 启动对弈程序,选择黑/白为 leelazero Engine,如下图所示

关于 leela-zero 的参数如下所示

Based on Linux (Research)

编译安装 Leela-Zero

1
2
3
git clone https://github.com/gcp/leela-zero
cd leela-zero
git submodule sync && git submodule update --init --recursive

使用 Dockerfile 安装 Leela-Zero

从 nvidia/cuda 开始构建 docker image,如下为 Dockerfiles/Dockerfile.gpu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
FROM nvidia/opencl:devel-ubuntu16.04

RUN echo "deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial main restricted universe multiverse" > /etc/apt/sources.list && \
echo "deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse" >> /etc/apt/sources.list && \
echo "deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse" >> /etc/apt/sources.list && \
echo "deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-security main restricted universe multiverse" >> /etc/apt/sources.list

# Install
RUN apt-get -qq update
RUN apt-get install -y apt-utils cmake g++ curl
RUN apt-get install -y libboost-all-dev libopenblas-dev opencl-headers ocl-icd-libopencl1 ocl-icd-opencl-dev zlib1g-dev
RUN apt-get install -y qt5-default qt5-qmake

RUN mkdir -p /src/build/
COPY . /src/
WORKDIR /src/build/

# GPU build
RUN CXX=g++ CC=gcc cmake ..
RUN make all -j$(nproc)

# CMD cmake --build . --target leelaz --config Release -- -j2

在 leela-zero 根目录执行

1
nvidia-docker build -t leelaz:gpu -f Dockerfiles/Dockerfile.gpu .

运行自对弈程序(Self Play)

通过 nvidia-docker 启动自对弈程序(nvidia-docker 安装参考 NVIDIA/nvidia-docker),执行如下命令

1
2
3
4
5
nvidia-docker run -it \
-v $HOME/data/leelaz/self_play:/data \
-v $HOME/data/leelaz/weights:/src/build/networks \
leelaz:gpu \
./autogtp/autogtp -u 4 -k /data/sgf -d /data/debug

命令解释:

  • -v:挂载自对弈棋面数据的存储路径;挂载权重文件。autogtp 默认在启动路径下找到 networks 路径加载权值。实际执行如下命令启动 leelaz

    1
    ./leelaz --tune-only -w networks/ --gpu=...
  • -u:Index of the GPU to use for multiple GPUs support

  • -k:存储每局自对弈数据的路径。默认情况下,自对弈的数据会先上传到 http://zero.sjeng.org/submit-match,然后再存储到本地。 如果没有指定 -k 参数,自对弈数据直接被删除。

使用 Tensorflow 训练模型(Training)

生成训练数据有如下两种方法

  1. 运行自对弈程序,得到对弈数据,如下所示

    1
    2
    $ ls ~/data/leelaz/self_play/
    257fab047b414591b6d2f1a8eb435516.sgf

    将自对弈数据转变为训练数据

    1
    2
    3
    4
    5
    6
    7
    8
    nvidia-docker run -it \
    -v $HOME/data/leelaz/self_play:/data \
    -v $HOME/data/leelaz/weights:/src/build/networks \
    leelaz:gpu ./leelaz \
    -w /src/build/networks/dec5b9d88877fe876a308b42370335c8f5465235d0104c889bdda3b42993aa28

    dump_supervised /data/257fab047b414591b6d2f1a8eb435516.sgf /data/train.out
    <CTRL+C exit>
  2. 在自对弈过程中设置 debug 目录,运行

    1
    ./autogtp/autogtp -u 4 -k /data/sgf -d /data/debug

    在 /data/debug 目录下,除开以 .debug.txt.0.gz 结尾的数据,以 .txt.0.gz 结尾的数据为训练数据。

启动 tensorflow-py3 进行训练

1
2
3
4
5
6
7
8
9
LEELAZ_HOME=`pwd`
nvidia-docker run -it --rm \
-v $HOME/data/leelaz:/data \
-v $LEELAZ_HOME:/src \
tensorflow/tensorflow:1.10.1-gpu-py3 /bin/bash

cd /src/training/tf
python parse.py /data/self_play/debug/*[!debug].txt.0.gz \
--test /data/self_play/debug/*[!debug].txt.0.gz

在 Leelaz 中使用新模型

启动 leelaz 加载新模型进行对弈,如下所示

1
2
3
4
./leelaz --gpu=0 --noponder -w ../training/tf/leelaz-model-2000.txt
genmove b
genmove w
...

Errors

  1. 更新 apt 源出现网络错误。

    修改 Dockerfile,添加国内的 apt source,例如 tsinghua 的 ubuntu 源。

  2. clinfo 没有找到 GPU device

    • 选择安装了 OpenCL 驱动的镜像,在 nvidia/opencl hub 中,选择合适的镜像源,在本文中选择 devel-ubuntu16.04 作为基础镜像

    • 使用 nvidia-docker 启动镜像,如下所示

      1
      nvidia-docker run -it --rm nvidia/opencl:devel-ubuntu16.04 /bin/bash

采用矿池进行虚拟货币挖矿 v.s 独立挖矿

发表于 2018-07-05 | 更新于 2018-12-10 | 分类于 funny |

本文简述了采用矿池挖矿和 Solo 方式挖矿的方法。 综合比较而言,对于小算力的运行环境(1 GH/s),矿池挖矿具有少量的稳定收益(nanopool 为例,约 0.08 ETH/day)。 Solo 方式在主链上与大矿池竞争算力,一天能挖到一个区块的概率不足 2%。

矿池挖矿 v.s 独立挖矿

采用矿池进行挖矿

该方案可以看做是将算力租借给矿池,矿池根据你贡献的算力给与提成,提成的方式是将挖出的代币按照比例转移到挖矿账户上。这种方案需要的条件包括:

  1. 可以挖取代币的硬件环境和挖矿软件
  2. 代币地址
  3. 可加入的合适矿池

本说明以 nanopool 矿池和基于 MetaMask 的 Ethereum 地址为例对挖矿过程进行阐述。

首先,需要一个可以挖矿的硬件环境,本文默认认为已经有一台安装好 NVIDIA GPU 的服务器。 其次,准备好自己的加密货币地址。该地址可以是交易所的充币地址,可以是 MetaMask 的 Ethereum 地址,还可以是本地钱包的地址,因为最后需要矿池将代币转移给你自己。以 MetaMask 为例,直接复制 Ethernet 的 Address (Copy Address to Clipboard),可以得到该地址。 再来,需要选择合适的挖矿软件。本文选择如下软件进行挖矿

  • ethminer 作为 Ethereum 的挖矿软件
  • xmr-stak 作为 Monero 的挖矿软件

最后,配置好参数运行挖矿软件就可以了。相关配置参数如下:

  • ethminer 的配置参数如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    export GPU_FORCE_64BIT_PTR=0
    export GPU_MAX_HEAP_SIZE=100
    export GPU_USE_SYNC_OBJECTS=1
    export GPU_MAX_ALLOC_PERCENT=100
    export GPU_SINGLE_ALLOC_PERCENT=100

    addr=....
    worker=....
    email=....

    ./ethminer --farm-recheck 2000 -U \
    -P stratum1+tcp://${addr}@eth-asia1.nanopool.org:9999/${worker}/${email}
  • xmr-stak 的配置参数如下

    1
    2
    3
    4
    5
    addr=....
    worker=....
    email=....

    ./xmr-stak -O xmr-asia1.nanopool.org:14433 -u ${addr}.${worker}/${email} --currency monero7

值得注意的是上述配置中的 worker 和 email 需要正确填写,之后在 nanopool 网站上,根据这两个值可以调整最小提取额度。

收益

以 ETH 为例。 nanopool 在7月9日当天的 ETH 平均算力为 42122.7 Gh/s,每 24 小时挖到的普通块数为 686,叔块数为 315。 省略叔块的奖励,在当天 1 Gh/s 的算力情况下,可以分得的收益为 $ 5 \times 686/42122.7=0.081428778 $ ETH。

独立节点挖矿

独立挖矿的好处在于挖到 Eth 所得的奖励独享,坏处在于需要与大矿池竞争算力。本文中设置的挖矿拓扑为一个主网节点和多个挖矿节点。其中,主网节点用来同步全账本信息,运行 geth 程序;挖矿节点仅负责计算,运行 ethminer 程序。以下是账本同步与挖矿运行的脚步:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 主网同步节点
./bin/geth --rpc \
--syncmode "fast" \
--rpcaddr "0.0.0.0" \
--rpcport "8545" \
--rpccorsdomain "*" \
--datadir "/mnt/data/eth-data/data" \
--ethash.dagdir "/mnt/data/eth-data/dag" \
--etherbase "..." \
--rpcapi "personal,db,eth,net,web3" \
console 2>&1 | tee geth.log

# 主网节点开放端口
sudo iptables -A INPUT -p tcp \
--dport 8545 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
或
sudo ufw allow 8545

# 挖矿节点
wallet_addr="..."
ethnet_addr="..."

## use nvidia gpu
./ethminer --farm-recheck 2000 \
-U -P http://${wallet_addr}@${ethnet_addr}

关于挖矿的可能性可以参考 Ethereum 的算力统计图。 从15年以来,入块的最低算力是 11.5297 GH/s。 以 7月9日当天的平均算力为 292711.4538 GH/s。 如果是 1GH/s 的算力在7月9日当天挖到一个区块的可能性为 $ 1/292711.4538 \times (60 \times 60 \times 24)/15=0.019678082 $ (ETH 区块产生速率为 15 秒一个 Block)。

参考资料

  1. Ethereum Homestead中文文档/挖矿

Express + MongoDB + PugJs 开发总结

发表于 2018-03-23 | 更新于 2018-12-10 | 分类于 development |

前段时间用 Express (PugJS) + MongoDB + Scrapy 建了一个视频索引网站。现在简单总结一下。

搭建网站

前端开发

首先,不得不说使用 MongoDB 建站是一件很方便的事情。后台 Model 有任何改动,只需要在 mongoose 加一个字段就可以了,相比于关系型数据库的严格类型约束,Object DB 更适合迭代开发。不过在性能上 mongodb 在后期有些吃力,特别是服务器配置不高的时候,mongodb 对内存的压力还是不小,不过这都是后话。在实现 search 和 related document 功能的时候,偷懒使用 MongoDB 的全文检索功能,性能不高,基本可以,适用于原型设计。

其次,Express 管理服务端逻辑。我采用传统的 MVC 将文件分为 controller、model、view 三个部分。controller 负载页面响应和数据获取,model 就是定义 mongoose 中 document 的字段,以及一些基本的存储逻辑(比如用户登录的密码验证,加密等操作),view 这部分我采用的 pugjs 进行前端页面渲染,pugjs 本质上还是 javascript 所以操作前端页面按照 JS 的逻辑来写就行了,不过开发过程中自己会把很多字符串处理逻辑都写在 PUG 中,感觉不是太好。

再次,就是前端 JS 的 IE 兼容问题。因为习惯使用 es2015 来写,在实际运行的时候发现在 chrome、firefox 下跑的很好,但是在 IE 上就出现各种问题。解决方法是用 babel 的 es2015-ie 插件进行兼容转换。

最后,Mobile 和 Destop 的支持,采用 Bootstrap 进行响应式布局,对于 CSS 不是特别熟悉,所以没有过多的研究响应式布局的细节。

数据获取

建站的前提是有稳定的数据源,因为自己不做 CMS,所以内容方面全部依赖爬取站点的内容质量。在此,不做详述。

数据爬取使用 scrapy 框架,只是使用了基本的功能。对于分布式、IP 池等没有涉及。每个站点都写了一个 Spider。从 Spider 到 Server Model 还有一段路要走,也就是说爬下来的时候需要清洗才能入库呈现。这部分的 pipeline 我是这么设计的:

  1. scrapy 爬取元数据,包括视频播放连接和元信息两部分;
  2. 经过 join 将视频信息和元信息进行合并,然后剔除不符合要求的数据,进入 model 库;
  3. 对数据进行回查,相当于是对 model 库进行清洗,主要就是剔除失效的视频连接,这部分可以定期处理(一个简单粗暴的 CMS 功能)。

写在最后

至此,站点就被搭建起来了,是不是很简单。因为没有数据维护(或者可以说是很简单的数据维护),一切数据依赖被爬取站点的数据质量。这种寄人篱下的站点建设方法不知道什么时候就会因为目标网站的升级需要更新自己的 spider 逻辑。

而且,随着 mongodb 中的数据越来越多,查询性能和 search 性能都在下降(前提是服务器不升级,512 MB 内存 1 CPU,6万多条记录,related search 的响应时间在 10-20 sec,find by index 的响应时间在 1-3 sec, findById 的的响应时间在 200-600 ms)。

此外,网站监控使用第三方服务,包括 google analysis、whos.amung.us、histats、clustrmaps 等,whos 主要监控实时的视频播放人数,histats 监控网站的访问,clustrmaps 这个主要是对旧版页面的监控。google analysis 分析整个网站的访问情况。

SEO:设置了 sitemap 和 robots,对页面的 meta 进行了简单的设计,但效果不是太理想,现在也在摸索。

反爬虫:还没做。

未来发展

这个站点就这样了吧,作为一个练手的站点,未来最多也就是更新数据源了。不过以后想用 vue 或者 react 写个单页面的网站。将前端和后端数据完全分离,现在 pug 渲染的时候,还是会将一部分 control 逻辑混写在 view 中。期待前后端数据和渲染的完全分离。

最后贴一个现在 clustrmaps 统计的访问情况,以示纪念。

clustrmaps

Mac 使用笔记

发表于 2017-11-01 | 更新于 2018-12-10 | 分类于 tools |

使用 Mac 时的一些经验分享

关于代理

科学上网(shadowsocks + VPS)

搭建 shadowsocks 代理服务器

在 DigitalOcean 买一个 VPS(5$/mo),然后安装 shadowsocks,网上有很多教程(例如 这个),再次不再累述。

使用 supervisor 管理 shadowsocks,配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# cat /etc/supervisor/conf.d/shadowsocks.conf

[program:shadowsocks]
command=ssserver -c /etc/shadowsocks.json
autorestart=true
user=nobody
stdout_logfile=/tmp/shadowsocks.out.log
stderr_logfile=/tmp/shadowsocks.err.log

# cat /etc/shadowsocks.json

{
"server":"::",
"server_port":8836,
"local_port":1080,
"password": "xx_x_xx",
"timeout":600,
"method":"aes-256-cfb",
"fast_open": false,
"prefer_ipv6": true
}

本地下载安装 shadowsocks 客户端,然后如下图所示进行配置。

shadowsocks-client

参考 supervisor 进程管理

关于 shadowsocks 安装错误

  1. ShadowSocks 启动时,会报错 undefined symbol EVP_CIPHER_CTX_cleanup

    • 错误信息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      2018-12-09 12:38:00 INFO     loading libcrypto from libcrypto.so.1.1
      Traceback (most recent call last):
      File "/usr/local/bin/ssserver", line 11, in <module>
      sys.exit(main())
      File "/usr/local/lib/python2.7/dist-packages/shadowsocks/server.py", line 34, in main
      config = shell.get_config(False)
      File "/usr/local/lib/python2.7/dist-packages/shadowsocks/shell.py", line 262, in get_config
      check_config(config, is_local)
      File "/usr/local/lib/python2.7/dist-packages/shadowsocks/shell.py", line 124, in check_config
      encrypt.try_cipher(config['password'], config['method'])
      File "/usr/local/lib/python2.7/dist-packages/shadowsocks/encrypt.py", line 44, in try_cipher
      Encryptor(key, method)
      File "/usr/local/lib/python2.7/dist-packages/shadowsocks/encrypt.py", line 83, in __init__
      random_string(self._method_info[1]))
      File "/usr/local/lib/python2.7/dist-packages/shadowsocks/encrypt.py", line 109, in get_cipher
      return m[2](method, key, iv, op)
      File "/usr/local/lib/python2.7/dist-packages/shadowsocks/crypto/openssl.py", line 76, in __init__
      load_openssl()
      File "/usr/local/lib/python2.7/dist-packages/shadowsocks/crypto/openssl.py", line 52, in load_openssl
      libcrypto.EVP_CIPHER_CTX_cleanup.argtypes = (c_void_p,)
      File "/usr/lib/python2.7/ctypes/__init__.py", line 379, in __getattr__
      func = self.__getitem__(name)
      File "/usr/lib/python2.7/ctypes/__init__.py", line 384, in __getitem__
      func = self._FuncPtr((name_or_ordinal, self))
      AttributeError: /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1: undefined symbol: EVP_CIPHER_CTX_cleanup
    • 原因

      openssl 升级到 1.1.0 以上版本,导致 shadowsocks2.8.2 启动报 undefined symbol: EVP_CIPHER_CTX_cleanup 错误。

    • 解决方法

      修改 /usr/local/lib/python2.7/dist-packages/shadowsocks/crypto/openssl.py,将 CIPHER_CTX_cleanup 替换为 CIPHER_CTX_reset (应该有两处)

      参考方案

使用 Proxifier 全局代理

shadowsocks 只能实现 socks 代理,限制了很多应用的使用。Proxifier 可以让不支持通过代理服务器工作的网络程序能通过HTTPS或SOCKS代理。 Proxifier 的使用非常简单,装好之后写 rules 就可以了。如下图所示,目前我只是将 Google Drive 的请求转发到 socks 上

proxifier-example

让 iOS 使用 SSH 代理访问 Web 服务

思路是在本地局域网内想办法建立一个 SSH 代理,然后让 iOS 设备将请求转发到这个代理上。

我们在一台 Macbook 和一个 iPad 的环境下实现这样的操作。具体操作步骤如下:

  1. 在 Macbook(IP 地址是 192.168.0.8) 上搭建一个 SSH 代理

    ssh -g -N -D 192.168.0.8:8000 mypc # -g 表示外部机器可以连到这个地址

  2. 创建一个PAC文件 proxy.pac,指定如何配置proxy。

    1
    2
    3
    function FindProxyForURL(url, host) {
    return "SOCKS 192.168.0.8:8000";
    }
  3. 在 Macbook 上开启 web 服务,并将 proxy 文件放在 web 服务上。

    cp proxy.pac /Library/WebServer/Documents sudo apachectl start

    可以通过 http://192.168.0.8/proxy.pac 访问这个代理文件。

  4. 在iOS上设置。

    通过“Settings”->”WLAN Networks”找到现在连上的Wifi,查看details(蓝色箭头)在最下面有设置http proxy的地方。选择”Auto”, 填入 http://192.168.0.8/proxy.pac (注意:需要添加 http 头)

Done!

这样在 Macbook 开启代理的情况下,iOS 设备可以通过该代理访问web服务。

参考:

  1. Mac OS X 启用 Web 服务器
  2. 让iOS使用SSH Tunnel

关于 WebDav

参考 How To Configure WebDAV Access with Apache on Ubuntu 14.04

关于工具

  • Alfred

    常用插件

    • Dict
    • Kill Process
    • TerminalFinder
    • IP Address
  • Atom

    常用插件

    • atom-beautify
    • autocomplete-python
    • vim-mode-plus
    • platformio-ide-terminal
    • go-plus
    • Remote FTP
  • Visual Studio Code

  • Zotero (这在 Zotero 使用笔记 讲过)

  • Vim

    环境配置使用 The Ultimate vimrc

  • Emacs

    环境配置使用 spacemacs

SSH 转发与代理

发表于 2017-10-17 | 更新于 2018-09-07 | 分类于 tools |

利用 SSH 可以方便的搭建本地代理访问互联网。本文介绍如何跨节点访问远程主机,并在此基础上建立代理。

Secure Shell(缩写为SSH),由IETF的网络工作小组(Network Working Group)所制定;SSH为一项创建在应用层和传输层基础上的安全协议,为计算机上的Shell(壳层)提供安全的传输和使用环境。
— from wikipedia

基于 SSH 的本地代理

使用 SSH 进行本地转发

在命令行执行如下命令

1
$ ssh -N -D 8000 <remote-host>

这行命令的意思是在本地开放一个转发端口(-D) 8000,将发送到这个端口的信息转发(-N)给远程主机(<remote-host>)。

这相当于在本地建立了一个监听 8000 端口的代理服务。

在 Chrome 浏览器中,安装 SwitchyOmega 插件,建立一个 profile 是在本机 8000 端口使用 socks5 协议的代理,取名为 proxy8000。之后,浏览 web 的时候选择 proxy8000,便可以将请求通过 remote-host 进行转发访问了。

SSH 代理

当前从A登录到C需要先登录B再登录C,如下图所示

1
2
3
4
5
+-----+             +-----+             +-----+
| | | | | |
| A +-----------> | B +-----------> | C |
| | | | | |
+-----+ +-----+ +-----+

我们的任务是通过A直接登录C

1
2
3
4
5
+-----+             +-----+             +-----+
| | | | | |
| A +-------------------------------> | C |
| | | | | |
+-----+ +-----+ +-----+

思路是建立 SSH 代理。在本地编辑 ~/.ssh/config 文件,增加 ProxyCommand 选项,像下面这样:

1
2
3
4
5
6
Host target.machine
User targetuser
HostName target.machine
IdentityFile %d/.ssh/id_rsa

ProxyCommand ssh proxyuser@proxy.machine -W %h:%p

示例(注意分隔符是 tab 不是空格,粘贴后请自行修改):

1
2
3
4
5
6
Host mypc
User lf
HostName 10.2.5.160
IdentityFile %d/.ssh/id_rsa
# ProxyCommand ssh proxyuser@proxy.machine nc %h %p 2> /dev/null
ProxyCommand ssh nbtest@172.22.1.21 -W %h:%p

相当于我们以 nbtest 用户登录到 172.22.1.21 服务器上,再以 lf 用户登录到 10.2.5.160 上。如果从本地可以免秘钥访问 nbtest@172.22.1.21 服务器,那么我们在命令行键入 ssh mypc 的时候直接输入 lf 的登录密码即可登录到 10.2.5.160 服务器上。

跨节点转发

与2中所述的网络结构一样。我们有些服务在 B 上不能访问,在 C 上可以访问。按照2,在 B 上,建立 A 到 C 的代理,然后在 A 建立通过 C 的转发服务,即可访问 C 上可访问的资源。

1
$ ssh -N -D 8000 <remote-C-host>

Zotero 使用笔记

发表于 2017-10-13 | 更新于 2018-09-07 | 分类于 tools |

Zotero 是一个免费的文献管理工具。关于 Zotero 的使用推荐阳志平老师的博客:

  • (1) 文献管理软件Zotero基础及进阶示范
  • (2) 作为知识管理工具的Zotero
  • (3) 平板与社交:再谈研究辅助工具Zotero兼配套APP
  • (4) Zotero之Zotfile插件的使用
  • (5) 电子文献管理攻略
  • (6) 如何批量下载PDF

本文主要介绍几个比较有用的插件和自己平时的操作流程。

插件

Zotfile (link)

主要功能包括,文件重命名,导出 PDF 文件到指定目录,与云存储配合实现扩平台阅读。

Better Bib(La)Tex(link)(已过时,zotero 支持 clipboard 导入,看 tips5 )

Zotero 本身不支持 Bibtex 格式导入文章条目。该插件弥补了这一不足。

使用时,将 Bibtex 信息复制到 clipboard 中,然后选择 import from clipboard 即可。

zotero-import-from-bibtex

Markdown Here(link)

使用 markdown 编写文章笔记,然后用 ctrl+alt+m 组合键将文本变为 html。该插件需要自行编译 xpi 插件包。

Google Scholar Citation(link)

查找文章的引用数,将引用信息填入 extra 字段中。

P.S. 最新版本的插件可能会不工作,可以使用老版本的插件(例如 v1.8.4)。如果一次更新太多会被 Google 认为是 Robot,限制访问。

工作流

[预备] 同步到 webDAV 上(使用 digital ocean 的私服搭建)

  1. 文件导入(一般是 connector 或 bibtex);
  2. 批量下载 (chrono download),并创建索引(Create Bibliography from Item);
  3. 更新 scholar citation(Google Scholar Citation);
  4. 使用 Zotfile 发送到共享文件夹 (Google Drive),使用 IPad 浏览(PDF Expert)并记录笔记然后再用 Zotfile [Get from Tablet] 同步回 Zotero;
  5. 在条目中创建 notes ,使用 markdown 语法做笔记(markdown here)。

另外,阳的博客(6)中使用的 chrome 插件已经过时,更新为 Chrono Download Manager。

Tips

  1. 递归显示 collection 中所有项目信息 (Hidden Perferences)

    希望实现的功能是,点击一个 collection 可以显示 sub-collection 的项目。Zotero 默认情况是不显示的。这个问题之前有人在 Zotero 社区问过。

    解决方法是通过修改 Hidden Preference 文件中的 extensions.zotero.recursiveCollections 字段,将其修改为 true 即可。

    Zotero 的配置文件包括一般可用文件和隐藏文件。隐藏文件的修改可以进入 Advanced (Panel),选择 open:config 进行修改(当前 Zotero 版本是 4.0.29.15)。

  2. 显示 collection 目录下文件个数 (tips_and_tricks)

    To see how many items you have, click an item in the middle pane and Select All (Command-A on macOS or Control-A on Windows/Linux). A count of selected items will appear in the right-hand pane.

  3. 批量展开与折叠项目信息

    To determine total items, including child attachments and notes, click an item and press the + (plus) key to expand all parent items before using Select All. You can press - (minus) afterward to collapse all items.

  4. 查看一个条目在哪些目录(tips_and_tricks)

    To see all the collections an item is in, select the item and then hold down the “Option” key (Mac OS X), “Control” key (Windows), or “Alt” key (Linux). This will highlight all collections that contain the selected item.

  5. 从粘贴板导入条目(How does the Import from Clipboard feature work?)

    Import from Clipboard allows you to import items from the raw code of any supported format (RIS, BibTeX, CSL JSON, etc.). Many websites post the bibliographic data for items in their raw form. To add these items to your Zotero library, select and copy the code from the site, then import through the option in File menu in Zotero, or by the keyboard shortcut (Windows/Linux: Ctrl-Alt-Shift-I / Mac: Cmd-Option-Shift-I).

TODO List

关于插件:

  • voyant-export 可以对储存在Zotero里的文字信息进行进一步分析,生成云图或者其它数据可视化图像。

LF

7 日志
5 分类
15 标签
© 2018 LF
由 Hexo 强力驱动 v3.7.1
|
主题 – NexT.Pisces v6.4.1