ML入门-机器学习简介

ML入门-机器学习简介

1. 什么是机器学习

机器学习是人工智能的一个分支,主要关于构造和研究可以从数据中学习的系统。

这也说明,机器学习的本质是通过数据实现的,更进一步讲,是从大量无法手动推算出规律、分布、结构的数据中,利用 CPU 强大的计算能力、通过迭代等科学的计算方法来自动计算出某种类型数据的共性,并使用这些共性为新来的数据假设、模拟出其可能的结果。

机器学习的数据通常包括训练数据和校验数据,其中:

  • 训练数据用于建立机器学习模型,通常包括:① 输入(特征 X)和 ② 输出(标签 y)。
    注意:此处黑体 X 表示矩阵,且有一些模型可以没有输出 y。
  • 校验数据用于在模型初步建立完毕后检验模型的效果,常常从训练数据中分割一部分作为校验数据,与训练数据具有相同的分布和格式。

2. 机器学习的学习类型

  1. 监督学习[Supervised Learning]:从带标签的数据中学习。例:房价预测、邮件识别等。
  2. 非监督学习[Unsupervised Learning]:从无标签的数据中学习蕴含的结构。例:文档聚类等。
  3. 强化学习[Reinforcement Learning]:通过曾经执行的动作及带来的奖惩进行学习,强化学习的标签 y 具有延迟性。例:AI 下棋等,当前棋子带来的影响需要经过一段时间才会表现出来。

2.1 监督学习

(1)两种任务类型:

  1. 回归任务(Regression):输出 $y\in R$ 为连续值。
  2. 分类任务(Classification):输出 y 为离散值。

(2)学习目的:学习到一个 X 到 y 的映射 f,从而对新输入的 X 预测其输出

$\hat{y}=f(\textbf{X})$。

(3)监督学习的训练数据包含标签 y,即标签在训练数据中是可见变量。

(4)数据样式:

$D=\{\textbf{X}_i, y_i\}_{i=1}^N$

其中 D 为数据集,$X_i$ 与 $y_i$ 为第 i 个样本的输入与输出,N 为样本总数。

(5)Scikit-Learn 中监督学习的大致流程:

  1. estimator.fit(X_train, y_train)
  2. estimator.predict(X_test)
  3. estimator.score(X_test, y_test)

2.2 非监督学习

(1)两种任务类型:

  1. 聚类(Clustering)
  2. 降维(Dimension Reduction)

(2)学习目的:发现数据中的“有意义的模式”,亦称为“知识发现”。

(3)非监督学习的训练数据不包含标签 y,即标签在训练数据中为隐含变量。

(4)数据样式:

$D=\{\textbf{X}_i\}_{i=1}^N$

(5)Scikit-Learn 中非监督学习的大致流程:

  1. transformer.fit(X_train)
  2. X_train_trans = transformer.transform(X_train)
  3. X_test_trans = transformer.transform(X_test)

2.3 增强学习

(1)学习目的:从行为的反馈(奖励或惩罚)中学习,以找到一条回报值最大的路径。

(2)步骤:设计一个回报函数[Reward Function],若 Learning Agent 在执行一个动作后获得了较好的结果,则给予一些奖励(例如:回报函数为正),否则给予一些惩罚(例如:回报函数为负),驱使 Learning Agent 寻找出一条最终获得回报总和最高的学习路径。


3. 机器学习开发环境

机器学习开发通常分为两部份:本地开发及验证、真实训练;在探索和调参阶段通常都使用 Python 开发,而在大量数据的真实训练中,则可能使用 Python 或是 C/C++ 完成,并且个人设备已经很难完成对应工作了,因此开发环境的重点在于「本地开发」。为了开发环境的稳定性及更方便地获取依赖包,通常会使用 PyCharm / pyenv / Anaconda 等工具。

3.1 安装Anaconda

(1)下载及安装 Anaconda

进入 Anaconda 安装包归档,找到目标版本,以 Linux 下 64 位集成 Python3 环境的最新版本为例,其安装包名称为:Anaconda3-2019.03-Linux-x86_64.sh,在 SSH 终端中输入以下指令:

1
2
3
4
5
6
7
8
# 下载安装包(默认在用户目录下,通过切换当前目录更改下载路径):
wget https://repo.continuum.io/archive/Anaconda3-2019.03-Linux-x86_64.sh

# 安装 Anaconda(默认在用户目录内,通过切换当前目录更改下载路径):
bash Anaconda3-5.0.1-Linux-x86_64.sh

# 配置环境变量:
source .bashrc

在终端内输入 conda --version,正确显示 Anaconda 版本信息则表示安装成功。某些情况下可能需要手动配置环境变量:

1
2
3
4
5
6
7
# 对所有用户生效则编辑:/etc/profile
# 对当前用户生效则编辑 ~/.bash_profile

# 添加环境变量,保存退出后 source 一下,或是重启终端:
export ANACONDA_HOME=/home/root/anaconda3/bin # 如果以 root 用户安装,使用此环境变量
export ANACONDA_HOME=~/anaconda3/bin # 如果以当前用户安装,使用此环境变量(其他用户同理)
export PATH=${PATH}:${ANACONDA_HOME}

(2)修改 Anaconda 默认配置

Anaconda 默认存在一个名为 base 的虚拟环境,且每次启动 Terminal 时会自动激活 base 环境,可以通过 Shell 配置修改:

1
2
3
4
5
6
7
8
9
# 修改 Shell 配置(以 ZSH 为例,其他同理):
vim ~/.zshrc

# 如果希望默认不激活 Anaconda,则添加一行命令,启动时取消激活:
conda deactivate

# 如果希望自动激活,但命令行的标题不显示 base 环境名,可还原 Shell 默认标题(也可以自定义):
PS1="%n@%m %1~ %# " # ZSH 默认命令行标题
PS1="\h:\W \u\$ " # Bash 默认命令行标题

(3)新建 Anaconda 虚拟环境

1
2
3
4
5
6
7
8
9
# 首先激活 base 环境:
conda activate base

# 然后创建一个名为 demo 的虚拟环境,Python 版本为 3.7.x 最新版:
conda create --name demo python=3.7

# 激活和退出虚拟环境:
conda activate demo
conda deactivate

(3)管理 pip 依赖包

  • 查看当前已安装的 pip 依赖:pip list

  • 添加 -i 参数使得本次 pip 命令走镜像地址:pip install -i [mirrors_host] [package_name]

    • 清华镜像:https://pypi.tuna.tsinghua.edu.cn/simple
    • 阿里云镜像:http://mirrors.aliyun.com/pypi/simple/
    • 中国科技大学镜像:https://pypi.mirrors.ustc.edu.cn/simple/
    • 华中理工大学镜像:http://pypi.hustunique.com/
    • 山东理工大学镜像:http://pypi.sdutlinux.org/
    • 豆瓣镜像:http://pypi.douban.com/simple/
  • 修改 pip 配置文件使得全局 pip 命令均走镜像地址:

    • Mac / Linux 系统修改:~/.pip/pip.conf
    • Windows 系统修改或创建:C:\Users\XXX\pip\pip.ini
    1
    2
    3
    4
    [global] 
    index-url = https://pypi.tuna.tsinghua.edu.cn/simple
    [install]
    trusted-host=mirrors.aliyun.com
  • 常用的依赖包:

    • scikit-learn (Including scikit-learn, numpy, scipy, joblib)
    • pandas
    • numpy
    • nose
    • jupyter
    • Pillow
    • psutil
    • pygame
    • seaborn
    • matplotlib
    • caregory_encoders

3.2 彻底卸载Anaconda

(1)准备工作

  • 确认 conda 环境变量可用;
  • 退出 conda 环境;
  • 关闭 conda 相关功能;

(2)安装 Anaconda 命令行卸载工具

执行命令:

1
conda install anaconda-clean

(3)使用卸载工具卸载

1
2
3
4
5
# 先激活默认环境
conda activate

# 执行卸载
anaconda-clean

卸载过程会提示是否删除对应模块,彻底卸载则全部选 y 即可:

1
2
3
4
5
6
7
8
Delete .anaconda? (y/n): y
# 会自动生成一个备份,具体路径以实际为准
Backup directory: /Users/xxx/.anaconda_backup/xxx
Delete .conda? (y/n): y
Delete .condarc? (y/n): y
Delete .ipython? (y/n): y
Delete .jupyter? (y/n): y
Delete .matplotlib? (y/n): y

(4)删除残留文件

  • 删除卸载工具生成的备份文件:rm -rf /Users/xxx/.anaconda_backup/xxx
  • 删除安装路径下的文件:sudo rm -rf /Applications/anaconda3
  • 删除环境变量中与 Anaconda 有关的部份;
  • 删除可能存在的隐藏文件:rm -rf ~/.condarc ~/.conda ~/.continuum

3.3 配置Jupyter Notebook

使用命令 pip install jupyter 安装 Jupyter 到当前环境中,如果需要切换环境,可以先在 Anaconda 中新建环境并切换至新环境后执行命令;安装后可输入 jupyter notebook 启动,验证环境配置。

默认情况下,Jupyter Notebook 运行时的工作目录为当前用户根目录,每次运行都要手动切换到开发目录,可通过 Jupyter 配置文件来修改:

1
2
3
4
5
6
7
8
9
10
11
# 先备份原始配置文件(如果有的话),以防万一:
mv ~/.jupyter/jupyter_notebook_config.py ~/.jupyter/jupyter_notebook_config.py.BAK

# 生成新的配置文件:
jupyter notebook --generate config

# 修改配置文件:
vim ~/.jupyter/jupyter_notebook_config.py

# 修改以下行为自定工作目录:
c.NotebookApp.notebook_dir = 'XXX'

3.4 在云服务器中运行Jupyter

如果是在云服务器中配置 Jupyter,安装后通过远程连接启动 jupyter notebook 发现连不上 Jupyter 服务,这是因为服务器上的 Jupyter 还没有配置外网访问。配置方式如下:

(1)生成 Jupyter 配置文件:

1
2
3
4
# 非 root 用户:
jupyter notebook --generate-config
# root 用户:
jupyter notebook --generate-config --allow-root

(2)打开 ipython,创建一个登录 jupyter 的密码:

1
2
3
4
5
6
7
8
# 进入 ipython 环境
ipython

# 导入 password 模块
from notebook.auth import passwd

# 修改密码
passwd()

然后会要求输入一次密码和一次确认密码,输入过程屏幕上不会有任何显示。正确输入密码后,会输出一个哈希值(省略后半部分),请复制或记录这个值:

1
'sha1:5311cd8b9da9:70dd3321..............'

(3)修改 Jupyter 配置:

1
2
3
4
# 非 root 用户:
vi ~/.jupyter/jupyter_notebook_config.py
# root 用户:
# vi /root/.jupyter/jupyter_notebook_config.py

在打开的配置中添加以下参数:

1
2
3
4
c.NotebookApp.ip='*'
c.NotebookApp.password = u'sha1:5311cd8b9da9:70dd3321..............'
c.NotebookApp.open_browser = False
c.NotebookApp.port = 8888

其中 password 项输入上一步复制的哈希值,port 项推荐使用默认的 8888 端口,也可以自己定义。

(4)检查远程服务器配置:

云服务器上配置好 Jupyter 后可通过以下方式远程访问:

  • 在已登录的 SSH 远程连接中执行:jupyter notebook(root 用户需要附加 --allow-root 参数);
  • 本地浏览器访问:http://云服务器公网ip:端口(例如 http://127.0.0.1:8888);

如果仍无法通过远程访问,则可能是因为云服务器的安全组还没有配置,8888 端口(或自己设置的端口号)还没有开放给公网。设置安全组的过程如下:

  1. 进入云服务器控制台
  2. 进入安全组设置
  3. 添加安全组规则
  4. 选择协议类型为 自定义 TCP,输入端口号为 8888(或自定义端口号),授权 IP 设置为 0.0.0.0(即不限访问 IP,不论哪里的网络都可以连接访问云服务器的 Jupyter)

配置完成后,即可通过 SSH 终端或浏览器输入地址的方式进入服务器的 Jupyter Notebook 了!如果仍旧提示无法访问,且云服务器所选的 Linux 系统为 Ubuntu(其他系统没试过),或许是因为防火墙的缘故,通过以下方式关闭:

1
ufw disable

3.5 服务器后台运行Jupyter

在选择云服务器训练数据时,除了考虑到算力的问题,当然也有持续运行的问题。尽管通过设置 SSH 的会花时间已经可以使得服务器的 Jupyter 一直在激活状态,但如果是自己的工作电脑,甚至办公本,平时还有大量的工作需要使用电脑,,一直在本地挂着一个 SSH 会话不能关机不能断开,总还是不妥,更何况如果遇到突然断网、突然死机等问题时,会话一关就中断运行,想想就让人抓狂。为此,给服务器的当前任务配置一个后台运行就显得尤为必要了。

通常,在 Linux 中,可以用 nohup [command] 来保持一项任务不被挂起,使用 nohup [command] & 来将任务转至后台并保持不被挂起。指令执行后,Linux 会立即将任务转至后台运行,且返回输出一个进程号:PID,当我们需要手动停止该进程时,可以通过 kill -TRM [PID] 来终止该进程。同时,该进程的所有输出均会默认存储至当前目录下的 nohup.out 文件中,使用命令 tail -f nohup.out 来实时查看动态输出,或者也可以使用:nohup [command] > out.log & 命令将输出重定向至自定义的文件(本例为out.log)并查看。

这对于 Jupyter 任务来说也是一样的,我们可以通过将 Jupyter 任务转至后台持续运行来释放本地电脑的工作压力:

1
2
3
4
# 非 root 用户:
nohup jupyter notebook &
# root 用户:
# nohup jupyter notebook --allow-root &

这样,当我们在 Jupyter 开始一项耗时任务后,即可直接断开 SSH 会话。注意:在本地需要离线时,不能终止 Jupyter 的进程,而应该直接断开 SSH 连接。

另外还有一点,由于 Jupyter 本身的局限性,当一个正在运行任务的 Jupyter 页面被关闭(而没有关闭服务)后,再次打开,只能保留前一次的结果,而不能恢复任务运行过程,因此,使用 nohup 命令转至后台的 Jupyter 任务应当是具有完整结果的,而不能再带有需要交互的部分,例如:

  1. 将某个数据集预先确定好的特征进行哈希编码,并将编码完的数据重新输出存储。
  2. 将某个数据集预先确定好的特征进行哈希编码,并询问我是否存储,根据选择完成相应任务。

假设在任务开始后,本地立即断开 SSH 连接,并等待任务完成后再重新连接。那么:
任务 1 是合理的,因为任务过程不需要交互,重新连接后,可以在服务器的本地找到输出的已完成哈希编码的数据集。
任务 2 是不合理的,因为任务执行完编码后,不能完成交互因此任务将直接运行结束,重新连接后相当于白费力气。

另外,除了基本的后台防挂起 nohup 外,还有一些工具也可以用来保持任务现场并恢复,例如:screen 和 tmux,可参考附录【7】、【8】,但终究因为 Jupyter 本身的原因,还是不太适合这种长时间可恢复的后台运行模式,我的建议是通过 Python 脚本进行长时间的后台操作,并尽量减少交互部分,及时将后续可能要用的数据保存成文件,而在 Jupyter 上仅作为一些可视化或测试、调整等的工具使用。

3.6 服务器SSH会话保活

在使用云服务器的过程中,有时去做别的事了过了一段时间后再看 SSH 终端,发现先是没反应、无法输入,再过一会儿就提示连接已断开,这是因为默认情况下 SSH 会话有一个连接时间,一段时间后就会断开会话。可以通过以下方式修改时间,以达到 SSH 会话保活的目的:

(1)在远程服务器执行:TMOUT=0,该方式方便、快捷,但缺点是仅对本次登陆有效,退出 SSH 后重新登录则需要再次设置。

(2)修改ssh配置文件:

1
2
3
4
5
6
7
8
9
10
# 打开配置文件
vi /etc/ssh/sshd_config

# 找到 ClientAliveInterval,该参数指定了服务器端向客户端请求消息的时间间隔。默认是0,不发送
# 修改 ClientAliveInterval 的值,单位为秒。如设置 600,即每 10 分钟发送一次请求保持会话。
ClientAliveInterval = 600

# 找到 ClientAliveCountMax,该参数表示允许超时的次数。
# 如果发现客户端没有响应,则判断一次超时,请根据实际需要进行设置。比如设置为10,表示允许超时6000 秒 = 100 分钟。
ClientAliveCountMax = 10

3.7 服务器虚拟内存

在运行大量数据集的时候,很有可能报这个错:Memory Error,这个错在 Python 里面也不算罕见了,一般表示瞬间内存占用超过了可用内存,可通过 free -m 命令查看内存信息,如果云服务器选择了纯净的 Linux,很可能默认配置没有交换内存,好在现在的 ECS 大多都使用了固态硬盘,即便默认非固态硬盘的,升级成本也比较低,因此可以拿出一部分硬盘作为交换内存使用。

(1)创建swap文件:

1
2
3
4
5
6
7
8
9
10
11
# 在 /usr 目录下创建交换内存文件
cd /usr
mkdir swap
cd swap

# 查看目前总用量
ll

# 创建一个循环,每次复制 1024 字节,共复制 1024000 次(10G)并输出为 swapfile1 文件。
# if 表示 infile,of 表示 outfile,bs=1024 表示写入的每个块的大小为 1024B,即 1KB。
dd if=/dev/zero of=/usr/swap/swapfile1 bs=1024 count=1024000

(2)查看刚才创建的 swap 文件大小与路径:

1
du -sh /usr/swap/swapfile1

(3)将目标文件设置为 swap 分区文件:

1
mkswap /usr/swap/swapfile1

(4)激活 swap,立即启用交换分区文件:

1
swapon /usr/swap/swapfile1

至此交换内存已经设置好了,并已激活可用,但仅限本次开机可用,想要每次开机都自动加载这个交换内存,则需要修改 /etc/fstab 文件

(5)编辑文件 /etc/fstab 中的 swap 行:

1
vi /etc/fstab

/etc/fstab 编辑状态下(UUID 省略后半部分),添加下面这段的最后一行:

1
2
3
4
5
/dev/mapper/vg_localhost-lv_root   /         ext4    defaults        1 1
UUID=cef520a0-df77-4........ /boot ext4 defaults 1 2
# /dev/mapper/vg_localhost-lv_swap swap swap defaults 0 0
# 下面这行是关键:
/usr/swap/swapfile1 swap swap defaults 0 0

保存并重启系统后,再次输入 free -m 命令,即可看到多了一个 swap 内存,大小为 10G。


参考文献