万万没想到,用Shell脚本做AI批处理,真香

Posted AI派

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了万万没想到,用Shell脚本做AI批处理,真香相关的知识,希望对你有一定的参考价值。

点击上方 AI派 ”, 选择“ 设为星标
最新分享,第一时间送达!
万万没想到,用Shell脚本做AI批处理,真香

作者:Leon Wang, 现为中科院特别研究助理 (博士后),在 AI、数据科学和科学计算等方面相关的工程实践上积累了丰富的经验。
编辑:王老湿

大家好,我们的专栏已经发布了三篇:


前面两篇文章已经为大家介绍了Linux的Shell命令相关的知识,我们已经可以在命令行下单独的完成一些操作。但在实际工作中,我们通常还需要利用这些命令,结合一些逻辑控制编写AI批处理脚本。

本篇文章就来介绍Shell脚本编程。因为bash是目前最常见的Shell环境,下文都将以bash为例进行介绍。

脚本基础

编写脚本文件

Shell脚本文件本质就是一个纯文本文件,只要把我们常用的txt文件的后缀名改成sh,甚至不改(只要你自己不混淆就可以)就可以作为脚本文件来用。一般,sh脚本应注意以下几点:
首行的shebang符号:

#!/bin/bash

保存文件后,为方便脚本文件能直接和命令一样运行,需要执行chmod +x fileshebang的可执行权限的内容可以回顾专栏里的 这篇文章。

变量

  • 定义赋值

定义Shell变量的形式为:变量名=变量值。例如:

PATH1="/tmp"

这里,注意变量名不能以数字开头,中间不能有空格和标点符号,以及Shell里面的一些关键字。变量值可以通过一条命令的结果动态赋值,例如:

PATH1=$(pwd)

注意,pwd是查看当前路径的命令,通过$()的形式,把里面pwd的结果直接赋值给PATH1

  • 使用变量


Shell中需要用$变量名的形式使用变量。结合前面所讲的定义赋值,我们可以使用一个已有变量的值,直接赋给另一个变量:


PATH1=$PWD

注意,$PWD是系统维护的环境变量,和pwd效果一致。

  • 命令行参数

与很多命令行环境相似,Shell脚本需要对多个参数进行处理。我们编写脚本时,可以通过一些特殊的变量获取命令行参数。首先,我们先写一个脚本testpara.sh,内容如下:

#!/bin/bash

param1=$1
param2=$2
echo "Running :$0"
echo "Parameter1: $param1"
echo "Parameter1: $param2"

当我们运行./testpara.sh p1 p2后,显示结果如下:

Running:./testpara.sh
Parameter1: p1
Parameter1: p2

请注意,如果参数很多时,脚本最好支持缺省参数,这样就不用每个参数都必须写全。我们可以把testpara.sh略微改造下,支持缺省参数:

#!/bin/bash
param1=${1:-para1}
param2=${2:-para2}
echo "Running :$0"
echo "Parameter1: $param1"
echo "Parameter1: $param2"

我们直接运行命令./testpara.sh,看看不给参数值的效果:

Running :./testpara.sh
Parameter1: para1
Parameter1: para2

可以看到,参数的缺省值已经发挥作用。

在实际的AI批处理脚本中,缺省参数也很实用。例如,一些超参数的设置经常会有一些默认的经验值,常见的训练集、验证集和测试集划分是6:2:2,tensorflow常用的版本号是1.13.2(因为1.14.0的tf.keras的卷积函数有一点Bug)。在这些场合灵活运用缺省参数,会提高我们的工作效率。

  • 退出状态码

基本上所有命令在退出时,都会返回一个状态值,表示这条命令是否成功。类似于c里面的int functionreturn 1return 0一样。在Linux中,一般定义0表示成功,非0表示失败。在Shell环境里,可以用$?获取上条命令的状态。例如,我们在成功执行testpara.sh后,可以运行echo $?,Shell这时返回了0,提示我们命令成功。

常见的非0码包括:127表示命令没找到,126表示不可执行。

逻辑控制

  • if

Shell的if语句与其他语言的很类似,唯一需要注意的是语法,以及与if成对的关键字fi。我们通过pip命令来举例:

sudo pip3 install numpy

  if [ $? = 0 ]; then
    echo "Installation Successfully"
  else
    echo "Problems to install Essential packages, check this!"
    exit 1
  fi

注意,Shell脚本的语句段不需要括号包围。

pip3 是指明使用与系统中python3想关联的pip命令安装。一般系统默认的pip指向pip2,即python2对应的pip命令。一种更推荐的的使用方式是python -m pip install numpy,这样可以避免混淆pythonpip的关系。

  • case

Shell中的case语句与其他语言的case也很相似,是多分支选择结构。注意,与case对应的结束关键字是esac。我们来看一个判断输入数字的例子:

echo 'Please press a number to select options'
read num
case $num in
    1)  echo 'option 1'
    ;;
    2)  echo 'option 2'
    ;;
    3)  echo 'option 3'
    ;;
    *)   echo 'Other'
    ;;
esac

注意,read读取键盘输入,并保存在变量num中。同时,注意case语句选项的写法。

也许读者们已经发现,Shell的条件选择命令的结束词,都是该命令反过来拼写。比如ifficaseesac

  • 循环

上面的例子为我们展示了Shell中的多分支结构,但我们注意到这个脚本只能执行一次选择就退出。在其他语言中,我们可以使用循环不断重复这个选择过程。同样,Shell中可以这样实现:

echo 'Input a number(press q to quit)'
read num
while [ $num != "q" ];do
    case $num in
        1)  echo 'option 1'
            ;;
        2)  echo 'option 2'
            ;;
        3)  echo 'option 3'
            ;;
        *)   echo 'Other'
            ;;
    esac
    echo 'Input a number'
    read num
done

这里特别注意,while中的do其实需要另起一行,上面的例子是通过;把两行合并在一行,显的紧凑。同时,为了脚本不会死循环,设置了q作为退出键。最后的donewhile ;do的结束词。

编程语言中还有for循环,适合迭代执行:

for ((i=1;i<=5;i++))
do   
    echo $i
done

另外,for语句更好用的是后面跟一个数组形式的参数,类似python中的list。数组可以是数字组成的序列:

for i in $(seq 1 5) 
do   
   echo $i
done   

这里的seq命令可以生成15的序列,等价于{1..5}。在批处理时,我们经常需要对多个文件进行处理,可以使用ls返回一个文件名的序列,作为for的参数:

for i in `ls`
do   
   echo $i 
done  

当我们知道一个路径的一部分,还可以通过通配符进行遍历,迭代每个符合的路径:

for file in /tmp/*  
do  
    echo $file  
done  

for in这种形式,非常类似python中的形式。可以利用各种函数或命令来构造循环迭代。另外,循环命令的执行体是do开头,done结尾,这点需要与ifcase的反写结尾区分开。

函数

Shell中支持用户自定义函数,形式如下:

function ] funname [()]

{

    XXXXX;

    [return int;]

}

这里,方括号表示可选内容。即function关键词可以省略,直接写函数名()就可以定义:

test(){
 echo hello
}

test

特别注意的是,函数也支持参数,使用$1$2等变量进行处理。

应用示例

配置python环境

开发环境中的配置,是个琐碎机械的工作。使用批处理进行环境配置,是脚本编程最常使用的场景。这里我引用一个小的项目prashant2018/MLSetup中的脚本,可以通过下面命令下载:

wget https://raw.githubusercontent.com/prashant2018/MLSetup/master/MLSetup_python3.sh

该脚本的使用方法很简单,可以给出一个模块的名字,单个安装;也可以直接给all,进行批量的全部安装:

./MLSetup_python3.sh numpy
./MLSetup_python3.sh pandas
./MLSetup_python3.sh all

这个脚本提供了一键安装包括numpy等多种常用python库的功能,综合运用了本篇文章讲解的命令行参数、退出状态码、case和函数多个知识点。请读者仔细研究,模仿其中写法。

文本标注

CRF是统计机器学习中的经典序列标注方法。关于CRF的更多介绍可以查阅文末给出的参考资料。若需要更多相关理论方面的介绍可以直接阅读李航老师的《统计机器学习》一书。

  • 安装依赖库

我使用的CRF工具来源于taku910/crfpp项目。crfpp本身是C++库,也通过Swig提供了Python的封装库,但是安装过程复杂。为了方便起见,我将其重新封装成标准的扩展模块crfpy,可以通过pip install crfpy直接安装好。除了crf_test命令暂时没加进来,其他都与原crfpp相同。

整个脚本文件在文末给出的代码仓库中的src/basic-02-shell/crf/位置,文件名为do.sh

  • 数据处理

首先,为了让脚本出错后就终止运行,方便查看问题,需要在脚本的起始位置(shebang行后的第一个非注释行)设置:

set -e
cat allchina_addr.txt| awk '{$1=""}{$2=""}1'> input.data

通过shuf命令对数据集进行打散:

shuf input.data > shuffled_input


  • 划分数据集

按照7:3的比例,对数据集划分训练集和测试集:

split -l $[ $(wc -l shuffled_input|cut -d" " -f1) * 70 / 100 ] \
    shuffled_input crfdata_
mv crfdata_aa train.txt
mv crfdata_ab test.txt

因为这个数据集的标注格式和crfpp要求的不同,所以我准备了make_crfpp_data.py这个脚本进行转换:

echo converting
./make_crfpp_data.py -i train.txt -o  train.data
./make_crfpp_data.py -i test.txt -o  test.data


  • 模型训练

最后,调用crf_learn命令训练模型:

echo training
time crf_learn -p 30 template train.data model.crf 2>&1 | tee  train.log

注意,time是显示命令执行时间。其中的templatecrfpp需要的特征模板文件,后面的2>&1 | tee train.log整个实现了屏幕上打印日志信息,又同时保存在train.log中。读者们可以自己试验,通过拆解各部分来体会各个作用。

目前在crfpy中还没有封装crf_test命令,所以暂时没法直接进行验证集测试。在后面的文章中,会配合python模块封装技术的讲解,带着大家去实战一下,怎么样把这个命令也封装进来,让大家都学会如何自己把一个C++库封装为python模块。

后记

本篇文章首先简单介绍了一些Shell编程最常需要的一些基础知识,然后提供了两个例子。

  • 第一个例子是通过批处理自动配置Python环境,把前面几个知识进行了串联。但有个问题是,这个脚本直接把模块安装到了系统的python中,在实际工作中,不一定合适。下一篇文章会为大家介绍一种更推荐的方式——虚拟环境。

  • 第二个例子,是通过基于CRF的NER任务实战脚本,演示了Shell脚本的批处理。我们特别注意,在第二个例子中,除了转换标签格式部分额外写了Python脚本,模型训练使用了crf_learn命令,其余都是利用Linux常用的shell命令就完成了。特别是最后的模型训练的日志记录,模型训练的计时,都没有额外编程。同时,Shell脚本完成了比Python更高一层的“胶水语言”,把Python和C++实现的Shell命令与其他内部命令很好的衔接起来,完成一整套pipeline。

PS: 本文所需代码和演示数据,可通过https://github.com/ai-union/ai_engineering_practice_guide  中的src文件夹获得。本篇文章的代码文件夹是basic-02-shell,CRF的相关代码文件在crf中。

参考资料

prashant2018/MLSetup

(https://github.com/prashant2018/MLSetup)


如何轻松愉快地理解条件随机场(CRF)?- 简书

(https://www.jianshu.com/p/55755fc649b1)




/ 每日赠书专区 /


为了回馈一直以来支持我们的读者, “每日赠书专区”会每天从留言支持我们的读者中选择一名 最脸熟的读者来赠予实体书籍(包邮),当前通过这种方式我们已赠送出  3 0+ 本书籍。

1.脸熟的评判标准是根据通过留言的次数来决定的

2.留言时需要按照今日留言主题来用心留言,否则不计入总数

3.每日赠书专区会出现在AI派当天发布文章的头条或次条的文章末尾


如果不理解头条/次条的含义的读者可看下面的图。

万万没想到,用Shell脚本做AI批处理,真香



今天我们的每日赠书专区出现在“ 条”的位置上 书籍为 Linux实战

万万没想到,用Shell脚本做AI批处理,真香

本书简介:

本书中共有12个实际项目,包括自动备份与恢复系统、建立一个私有的Dtropbox风格的文件云以及构建你自己的MediaWiki服务器等。当你开展诸如虚拟化、灾难恢复、安全、备份、DevOps以及系统故障诊断等核心实践时,你将会接触到一些有趣的例子。每章都以回顾主要名词、安全实践、命令行以及习题结束。