読者です 読者をやめる 読者になる 読者になる

のんびりしているエンジニアの日記

ソフトウェアなどのエンジニア的な何かを書きます。

Rundeckを使ってリッチに定期ジョブを作った

皆さんこんにちは
お元気ですか。昨日食べた中華そばおいしかったです。

定期的に実行するJobを作成・実行する場合何を使っているでしょうか?
メジャーなツールであれば、cronがあります。

しかし、このツールはCUI、かつ、
他に色々追加したいこと(リトライ、通知)などを
設定したい場合に大変で、面倒です。

そこで、Rundeckです。RCO&RLSさんの勉強会に触発されて使ってみました。

Rundeckとは?

一言で表すとジョブスケジューラです。
ジョブを作成し、実行間隔の管理、通知、実行方式などを管理できます。

GUIで実行コマンドを管理できるので、非常に管理しやすく
過去のログも確認しやすい、成功失敗で通知ができるなど、様々なメリットがあります。

個人的に思いつく使い道は機械学習バッチ処理ですね!

Rundeckの起動

早速、実行用のjarファイルをダウンロードしましょう。

rundeck.org

このjarファイルを実行すれば立ち上がります。起動コマンドは次のとおりです。

java -jar rundeck-launcher-2.8.2.jar

起動時に必要な設定ファイル群も作られます。
まずは、config/rundeck-config.propertiesの7行目のgrails.serverURLを変更します。
但し、起動時に作られるconfigファイルはserverのURLが誤っているため、画面の遷移ができません。
ローカルで起動する場合は、localhostで大丈夫です。localhostの例を次に示します。

#rss.enabled if set to true enables RSS feeds that are public (non-authenticated)
rss.enabled=false
grails.serverURL=http://localhost:4440
dataSource.dbCreate = update
dataSource.url = jdbc:h2:file:/Users/Tereka/Programing/Software/Rundeck/server/data/grailsdb;MVCC=true

設定後に改めてサーバを起動しましょう。

Rundeckを使ってみる

まずは、アクセスします。http://localhost:4440へアクセスしましょう。
このアドレスを入力するとログイン画面に遷移します。こちらはID, PASS共に、adminを入力します。

f:id:tereka:20170520192935p:plain

ログインすると、プロジェクト管理画面に遷移します。

プロジェクトを生成する。

まずは、プロジェクトが存在しないため、プロジェクトを作成します。
緑色のボタン「New Project」をクリックします。

f:id:tereka:20170520193842p:plain

次にプロジェクトの設定をします。今回は「Project Name」を入れるのみで良いです。
下の方にある作成ボタンを押します。

f:id:tereka:20170520193902p:plain

これで、プロジェクトが作られました。ここからプロジェクトのジョブを作ります。

Jobの作成

これから実際に実行するジョブを作成します。

f:id:tereka:20170521231130p:plain

Workflowで実行したいコマンドを設定します。
試しに「ls /tmp」のコマンドを実行するジョブを構築します。
「Wolkflow」のAdd StepからCommandをクリックし、
Commandに「ls /tmp」を入力しましょう。

f:id:tereka:20170521231704p:plain

そして、Createボタンを押します。

実行&結果表示

作成したジョブを実行します。
緑色のボタンである「Run Job Now」をクリックしましょう。

f:id:tereka:20170521231917p:plain

実行すると結果を確認できます。ちゃんと成功していますね!
結果のタブは以下の意味があります。

  1. Summary:ジョブが成功した失敗したの概要
  2. Report:結果のレポート
  3. Log Output:標準の出力
  4. Definition:ジョブの定義

f:id:tereka:20170521231925p:plain

2つのコマンドを使い、片方を失敗する場合

前回はシンプルな実行をするジョブでした。しかし、次は少々なジョブを作成します。
「cat /tmp」と「ls /tmp」のコマンドを組み合わせます。前回と同じように追加していきます。

今回のポイントは、「Run remaining steps before failling」です。
これを選択することで、前段のコマンドが失敗した場合も継続して後段が実行されます。
今回は「cat /tmp」が失敗、「ls /tmp」が成功します。

f:id:tereka:20170521233017p:plain

実行結果です。この結果から2つのコマンドが実行され、
1件のコマンドが成功、1件が失敗していることがわかります。

f:id:tereka:20170521232830p:plain

これで、2つ以上のコマンドが実行できることを確認できました。

定期的に実行する

本記事最後にRundeckを利用した定期実行を行ってみます。
まずは、ジョブを作ります。今回はdf -hのディスク確認コマンドを実行するタスクを作ります

今回は「Schedule to run repeatedly?」の項目をYesにします。
これをYesにすることで、定期実行ジョブの設定が可能となります。
crontab形式の設定が可能のため、その設定を行います。設定を行った画面は次になります。

このcrontabの設定だと毎分、実行するコマンドになります。

f:id:tereka:20170521234230p:plain

実行画面に遷移し、時々リロードすると、画面が更新されて定期的に実行されることがわかります。

f:id:tereka:20170521234240p:plain

これでリッチな定期実行タスクの設定ができますね。

最後に

まだまだRundeck使いこなせていませんが、リモートのジョブを実行する、
通知機能(拡張すればSlackも)の実行などを行うことができ、非常に便利です。

やっぱり機械学習バッチを作る時に便利かも・・

Chainerの学習の様子をリモートで確認するExtensionを作った

皆さんこんにちは
お元気ですか。私はGWでリフレッシュして、生き返りました。

Kaggleをやっているとき(特に画像などの長い場合)にリモートで
今学習されているかどうか、誤差はどうかなどのモデルの
様子が気になることはありませんか?

私は画像認識系のコンペを実際に行っている時に、気になることがあります。
これどうしようかと考えていたのですが、歩いている時にふと思いついたので実装しました。
このアイデアの実装のために、新しいChainerのExtensionを開発しました。(Trainerを使う想定です)

イデア

Slackであれば外出中も見れると考えました。
そのため、学習の途中経過(lossなど)を投稿すれば見れる!
実装イメージは次の図に掲載しました。

f:id:tereka:20170509235100j:plain

コードを見た限りだと、Extensionで実装できそうだったので、トライしました。

Extensionの実装方法

Extensionの実装ですが、先に必要な情報を__init__に実装します。
そして、__call__が呼び出され、処理をする仕組みとなっています。

今回は既に実装されているPrintReportを参考に実装します。

前準備

SlackのWeb APIのIncoming Webhooksと投稿するChannelとusernameが必要です。
SlackのAPIに必要な情報は以下のurlから遷移して取得してください。

api.slack.com

コード

早速Extensionを実装しました。
表示情報は既に実装されているPrintReportと同じにしました。
_throw_slackにSlackに投稿する部分を定義しています。
__init__に初期設定で必要な情報、__call__に表示する部分を記載しました。

# coding:utf-8
import os
import sys
from chainer.training import extension
from chainer.training.extensions import log_report as log_report_module
import requests
import json


class SlackReport(extension.Extension):
    def __init__(self, entries, log_report='LogReport', username="", url="",channel="",out=sys.stdout):
        self._entries = entries
        self._log_report = log_report
        self._log_len = 0  # number of observations already printed
        self._out = out

        # format information
        entry_widths = [max(10, len(s)) for s in entries]

        header = '  '.join(('{:%d}' % w for w in entry_widths)).format(
            *entries) + '\n'
        self._header = header  # printed at the first call

        templates = []
        for entry, w in zip(entries, entry_widths):
            templates.append((entry, '{:<%dg}  ' % w, '  ' * (w + 2)))
        self._templates = templates
        self.username = username
        self.url = url
        self.channel = channel

    def __call__(self, trainer):
        if self._header:
            self._throw_slack(self._header)
            self._header = None

        log_report = self._log_report
        if isinstance(log_report, str):
            log_report = trainer.get_extension(log_report)
        elif isinstance(log_report, log_report_module.LogReport):
            log_report(trainer)  # update the log report
        else:
            raise TypeError('log report has a wrong type %s' %
                            type(log_report))

        log = log_report.log
        log_len = self._log_len
        while len(log) > log_len:
            self._observation_throw_slack(log[log_len])
            log_len += 1
        self._log_len = log_len

    def _throw_slack(self, text):
        try:
            payload_dic = {
                "text": text,
                "username": self.username,
                "channel": self.channel,
            }
            requests.post(self.url, data=json.dumps(payload_dic))
        except:
            self._out.write("error!")

    def serialize(self, serializer):
        log_report = self._log_report
        if isinstance(log_report, log_report_module.LogReport):
            log_report.serialize(serializer['_log_report'])

    def _observation_throw_slack(self, observation):
        text = ""
        for entry, template, empty in self._templates:
            if entry in observation:
                text += template.format(observation[entry])
            else:
                text += empty
        self._throw_slack(text)

使い方

APIに必要な情報の定義とExtensionにSlackReportを追加するのみです。
例えば、公式のMNISTサンプルでは次のExtensionを追記してください。

username,channel,urlはそれぞれ自分の設定を指定してください。

    trainer.extend(SlackReport(['epoch', 'main/loss', 'validation/main/loss',
                                'main/accuracy', 'validation/main/accuracy', 'elapsed_time'],
                               username="YOUR USER NAME",
                               channel="YOUR SLACK CHANNEL",
                               url="SLACK API URL",
                               ))

Slackへの投稿結果

ExampleのMNISTで実験したら、こんな感じになります。ちょっとガタガタなのはご愛嬌。
外出時に気になる場合は皆さんも使ってみましょう。

設定が間違っているときの処理は…、結構適当です。

f:id:tereka:20170509233656p:plain

CUDAを使ってGPUプログラミングに挑戦してみた。

皆さんこんにちは
お元気ですか。私は家の近くに一風堂ができて感動しています。
本日はCUDAを使ったGPUプログラミングに挑戦します。

最近、ふと思うことがあります。
GPUをよく使いますが、GPUの心みたいなものがわからない状態です。
そこで、GPUプログラミングに挑戦し、コードを書きながらGPUのことを
知ることができたらと思って書いてみました。

GPUわからないとTwitter呟いていたら、
@さんにおすすめの書籍を紹介してもらいました!
GPUがどんな仕組みで動作しているのか非常にわかりやすいです。

CUDA C プロフェッショナル プログラミング impress top gearシリーズ

CUDA C プロフェッショナル プログラミング impress top gearシリーズ

そもそもGPUって何?

第1回 GPUコンピューティングおよびCUDAについて | G-DEPより引用します。

GPUとはGraphics Processing Unit の略称で、その名の通りPCやワークステーションにおいて画像処理を担当する主要な部品の一つです。高速のVRAM(ビデオメモリ;グラフィックボード上のGPU専用メモリ)と接続され、グラフィクスシェーディングに特化したプロセッサが多く集まった構造を持っています。一つ一つのプロセッサの構造は単純なためその機能はCPUに比べて限定されたものですが、大量のデータを複数のプロセッサで同時かつ並列処理することができます。

近年、DeepLearingの演算で利用されることの多いのは最後にある大量のデータを
複数のプロセッサで同時かつ並列処理できる点でしょう。
これにより特に行列演算において、CPUより高速に演算を行えます。

ただし、GPUは条件分岐処理が含まれるとCPUより処理が遅くなる傾向があるとのこと。
条件分岐があると逐次処理になり、処理自身を並列にできないようです。

CUDAとは

第1回 GPUコンピューティングおよびCUDAについて | G-DEPより引用します。

CUDA(クーダ)とはCompute Unified Device Archtectureの略称で、半導体メーカーNVIDIA社が提供するGPUコンピューティング向けの統合開発環境です。プログラム記述、コンパイラ、ライブラリ、デバッガなどから構成されており、プログラム言語はC言語ベースに拡張を加えたものであるため、C言語によるプログラミングの経験があれば扱いやすくなっています。

GPUコンピューティング向けの環境であるようです。(そのまま)
CUDAはC言語ベースに拡張を加えたものであるため、C言語を扱ったことがある人であれば
簡単に実装することができるそう。

さっそく、CUDAを使ったGPUプログラミングに挑戦しましょう。

準備

GPUとCUDA、GPUドライバーを準備してください。

nonbiri-tereka.hatenablog.com

GPUHello Worldを書く

まずは、GPUHello Worldです。
次のソースコードを「hello_world.cu」のファイル名で作成しましょう。

#include <stdio.h>

int main( void ) {
  printf("Hello, World!\n");
  return 0;
}

そして、コンパイルします。コンパイラgccではなくnvccを使いましょう。

nvcc hello_world.cu

nvccは「ソースの中のCPUで動く部分とGPUで動く部分を分け、CPU用コードをCコンパイラに渡し、GPU用コードをコンパイル(正確にはPTXコードに変換)する」コンパイラとのことです。
そのため、Cのみでも、実行可能となります。

さて、実行しましてみましょう。

$ ./a.out
Hello, World!

これでGPU計算の第一歩を踏み出しました(ぶっちゃけ、GPU使ってない

GPU上で計算させてみる。

先程実行したのは出力をする簡単なものです。
GPUを使って計算させてみましょう。
次のコードは2つの配列を合計する計算をする処理を行います。

#include <stdio.h>
#include<stdlib.h>

#define N 2000000000

__global__
void sum_of_array(float *arr1, float *arr2, float *arr3, int size){
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    arr3[i] = arr1[i] + arr2[i];
}

void initialize_array(float *arr, int size){
    for (int i = 0; i < size; i++){
        arr[i] = (float)rand();
    }
}

int main(void){
    float *arr1, *arr2, *arr3, *d_arr1, *d_arr2, *d_arr3;
    size_t n_byte = N * sizeof(float);

    arr1 = (float *)malloc(n_byte);
    arr2 = (float *)malloc(n_byte);
    arr3 = (float *)malloc(n_byte);

    initialize_array(arr1, n_byte);
    initialize_array(arr2, n_byte);
    initialize_array(arr3, n_byte);

    printf("start cudaMalloc\n");
    cudaMalloc((void**)&d_arr1, N);
    cudaMalloc((void**)&d_arr2, N);
    cudaMalloc((void**)&d_arr3, N);
    printf("finish cudaMalloc\n");

    printf("start cudaMemcpy\n");
    cudaMemcpy(d_arr1, arr1, n_byte, cudaMemcpyHostToDevice);
    cudaMemcpy(d_arr2, arr2, n_byte, cudaMemcpyHostToDevice);
    cudaMemcpy(d_arr3, arr3, n_byte, cudaMemcpyHostToDevice);
    printf("finish cudaMemcpy\n");

    printf("start kernel function\n");
    sum_of_array<<<(N+255)/256, 256>>>(d_arr1, d_arr2, d_arr3, n_byte);
    printf("finish kernel function\n");
    cudaMemcpy(arr3, d_arr3, n_byte, cudaMemcpyDeviceToHost);
}

GPU上で演算するには、次の4つの手順が必要になります。

  1. GPUのメモリを確保する・・cudaMalloc
  2. GPUのメモリにコピーする・・cudaMemcpy(cudaMemcpyHostToDevice)
  3. 計算を実行する・・カーネル関数
  4. CPUのメモリに結果をコピーする・・cudaMemcpy(cudaMemcpyDeviceToHost)

まずは、GPUメモリを確保します。これはcudaMallocを使って行います。
次にGPUのメモリにCPUで確保したデータをコピーします。そして、カーネル関数と呼ばれる
関数で計算し、最後結果をCPUに書き戻します。

これにより、GPUを使った演算ができます。
CUDAにはまだまだライブラリがあるので、どんどん使ってみましょう!

Anacondaを使ったPythonの環境構築

皆さんこんにちは
お元気ですか。私は暇です(だと信じています)。
最近ブログタイトルと本人の忙しさが一致していないと言われるので
とりあえず暇ですといってみます。

本日は、Anacondaについて書きます。

Anacondaについて

Anacondaとは

AnacondaはPythonのデータサイエンスプラットフォームで
100以上のライブラリを簡単に導入できます。

Pythonのバージョンを仮想環境を使って切り替えることや
科学技術演算ライブラリ(numpyなど)のインストールを行うことを簡単にできます。
Windowsを全く利用していなかった頃は気にしていなかったのですが、
numpyやscipyを簡単に使えます。(pipで入れるとC環境の関係でハマったことがあります。)

インストール方法

まずは、インストールです。インストール用パッケージは
Anacondaの公式サイトで公開されています。
インストール先のOSによって媒体が異なるので、適切な媒体を選択しましょう。

Download Anaconda Now! | Continuum

GUIだとクリックするとインストールがはじまりますのでその手順に従いましょう。
また、CUIの場合は、公式にも掲載されている以下のコマンドを
利用すればインストールできます。
後半のshのファイルパスはインストール環境に併せて変更してください。

$ bash Anaconda3-4.2.0-Linux-x86_64.sh

但し、このコマンドは対話的に設定を行うので、Dockerfileのコマンドなど
対話的で行えない環境の場合は追加でオプションが必要です。
その場合は次のコマンドを利用しましょう。

$ bash Anaconda3-4.2.0-Linux-x86_64.sh -b -p /root/anaconda3

オプションですが、-bはバッチモードです。
例えば、Dockerfileに書く時のように対話的に実行できない場合に利用します。

  • pはAnacondaのインストール先のパスを設定します。

最後に環境変数を設定します。Anaconda環境を標準の初期設定にしたい場合は、
.bashrcに予めexport文を書いておきましょう。

$ export PATH=/root/anaconda3/bin:$PATH

そして、「python」とコマンドを叩き、以下のようになると成功です。
特に確認する必要がある項目はAnaconda 4.2.0が表示されていることです。

$ python
Python 3.5.2 |Anaconda 4.2.0 (64-bit)| (default, Jul  2 2016, 17:53:06)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

condaを使って環境を構築・確認する

Anacondaは付属のcondaでPythonの環境を作ったり、
新しいライブラリをインストールできます。
実際に構築してみましょう。

ライブラリのインストール

Anacondaにライブラリをインストールする時は、condaコマンドを使います。
まずは、conda searchを使い、インストールライブラリを確認します。
例えば、tensorflowをインストールする場合、次のようなコマンドを実行します。

$ conda search tensorflow
Fetching package metadata .......
tensorflow                   0.10.0rc0           np111py27_0  defaults
                             0.10.0rc0           np111py34_0  defaults
                             0.10.0rc0           np111py35_0  defaults

これらのパッケージをインストールするには、installコマンドを使います。

$ conda install tensorflow

但し、conda searchで見つからない見つからないライブラリはpipでインストールしましょう。

condaで入れたライブラリの一覧

conda listを入力するとライブラリの一覧を確認できます。

$ conda list
# packages in environment at /root/anaconda3:
#
_license                  1.1                      py35_1
_nb_ext_conf              0.3.0                    py35_0
alabaster                 0.7.9                    py35_0

仮想環境の構築と削除

仮想環境を構築します。今回は試しにPython3.6の仮想環境を構築します。

$ conda create -n py36 python=3.6 anaconda

構築した環境の確認は以下のようにできます。

$ conda info -e
# conda environments:
#
py36                  *  /root/anaconda3/envs/py36
root                     /root/anaconda3

また、構築した仮想環境ですが、removeコマンドで削除できます。

$ conda remove -n py36 --all
$ conda info -e
# conda environments:
#
root                  *  /root/anaconda3

仮想環境にライブラリをインストールする

インストールした環境にライブラリがない場合があります。
その場合、インストール方法は2つあります。

  1. conda searchを使い、検索し、インストールをする。
  2. pipを使ってインストールをする。

基本的には、conda searchしても見つからない場合はpipを使ってインストールします。

仮想環境の切り替え

この環境をactivateするには、sourceでこのコマンドを動かします。

$ source activate py36

逆に環境を無効化したい場合はdeactivateを使います。

$ source deactivate

condaのupdate

conda自身を更新することができます。
まずは、versionを確認しましょう。

$ conda --version
conda 4.2.9

次にversionを更新します。非常に簡単で、conda updateを使います。

$ conda update --prefix /root/anaconda3 anaconda

conda自身の情報

conda自身がどのような状態かは、conda infoを使えば確認できます。

$ conda info
Current conda install:

               platform : linux-64
          conda version : 4.3.13
       conda is private : False
      conda-env version : 4.3.13
    conda-build version : 2.0.2
         python version : 3.5.2.final.0
       requests version : 2.12.4
       root environment : /root/anaconda3  (writable)
    default environment : /root/anaconda3
       envs directories : /root/anaconda3/envs
                          /root/.conda/envs
          package cache : /root/anaconda3/pkgs
                          /root/.conda/pkgs
           channel URLs : https://repo.continuum.io/pkgs/free/linux-64
                          https://repo.continuum.io/pkgs/free/noarch
                          https://repo.continuum.io/pkgs/r/linux-64
                          https://repo.continuum.io/pkgs/r/noarch
                          https://repo.continuum.io/pkgs/pro/linux-64
                          https://repo.continuum.io/pkgs/pro/noarch
            config file : None
           offline mode : False
             user-agent : conda/4.3.13 requests/2.12.4 CPython/3.5.2 Linux/4.9.4-moby debian/jessie/sid glibc/2.19
                UID:GID : 0:0

最後に

一通りAnacondaを使った環境構築をやってみました!
・・・大変だった。。

Cookiecutterを使ってテンプレートからプロジェクトを作成する

皆さんこんにちは
お元気ですか。ユニクロの極暖Tシャツ着ているとかなり暑い。。。冬なのに

今日は、Cookiecutterを使ったテンプレートからの複製生成を試みます。
テンプレートからちょっと変えたい部分を用意することで、簡単に
似たプロジェクトを作成できます。

Cookiecutterについて

Cookiecutterは雛形となるテンプレートプロジェクトから
プロジェクトを生成するツールです。
雛形となるプロジェクトを作ってしまえば、少し変更したい箇所を指定して
新しいプロジェクトを作ります。

github.com

インストール

cookiecutterをpypiから取得できます。

sudo pip install cookiecutter

Cookiecutterを使った複製をやってみる。

頻繁に利用がありそうなテンプレートの雛形は既に用意されています。
とりあえず、何も考えずに作ってみましょう。

GitHub - ionelmc/cookiecutter-pylibrary: Enhanced cookiecutter template for Python libraries.の雛形を使用する場合は
次のように実行します。

cookiecutter gh:ionelmc/cookiecutter-pylibrary

途中で色々と聞かれるので必要のある箇所は入力します。

full_name [Ionel Cristian Maries]: tereka
email [contact@ionelmc.ro]:
website [https://blog.ionelmc.ro]:
github_username [ionelmc]:
project_name [Nameless]:
repo_name [python-nameless]:
package_name [nameless]:
distribution_name [nameless]:
project_short_description [An example package. Generated with cookiecutter-pylibrary.]:
release_date [today]:
Select year:
(略)

他にもflaskやdjango、restのプロジェクトがあります。
これで、確認すると変数となっていた箇所が指定した名前に
置き換わっていることを確認できます。
次はテンプレートを構築してみます。

テンプレートを作成に挑戦してみる。

準備されているテンプレートでは不足することもあるでしょう。
そんなときのために、自分で1からテンプレートを作ってみます。

ディレクトリの雛形は以下の通りです。

ディレクトリ構成

treeコマンドを使ってcookiecutterの雛形の構成を表示します。

.
└── TestProject
    ├── cookiecutter.json
    └── {{cookiecutter.project_name_lower}}
        └── README.md
cookiecutter.json

cookiecutter.jsonにcookiecutterの雛形で使う変数を定義します。

{
  "project_name" : "test-project",
  "create_document": "y",
  "license": ["MIT", "BSD-3", "GNU GPL v3.0", "Apache Software License 2.0"],
  "project_name_rower" : "{{ cookiecutter.project_name|replace('-', '_') }}"
}

{{ cookiecutter.<変数名> }}とすることで、その変数を埋め込めます。
また、埋め込んだ変数の文字を"-"→"_"に置換しています。
licenseの箇所はリストにしています。リストをjsonで入力した場合、
対話的に実行する時に候補を表示します。

README.md

プロジェクト内にあるREADME.mdにcookiecutterで
定義した変数を埋め込むテストをします。
今回、テストとして用意した文書は以下の通りです。

{{ cookiecutter.project_name}} document.

{% if cookiecutter.create_document == 'n' -%}
   No Document
{%- endif %}

{% if cookiecutter.create_document == 'y' -%}
{{cookiecutter.license}}

This is an apple.
{%- endif %}

このREADME内で、cookiecutterの変数を使えることは同じです。
追加で、if文を使った条件式を構築できます。上記の場合、
{% if cookiecutter.create_document == 'n' -%}はcreate_document変数がnの場合に動作し、
{% if cookiecutter.create_document == 'y' -%}はcreate_document変数がyの場合に動作します。

実行

コマンドラインからcookiecutter <ディレクトリ>で実行できます。
但し、ディレクトリ内にcookiecutter.jsonがないと実行できません。

$ cookiecutter TestProject/
project_name [test-project]: test-project
create_document [y]: y
Select license:
1 - MIT
2 - BSD-3
3 - GNU GPL v3.0
4 - Apache Software License 2.0
Choose from 1, 2, 3, 4 [1]: 2
project_name_lower [test_project]:

cookiecutter.jsonにリストで定義した設定は番号で選択できるようになっています。

作られたプロジェクトを確認する

最後に作ったプロジェクトを確認します。
まず、cookiecutterの変数になっていたディレクトリが
「test_project」の名前になりました。

.
└── test_project
    └── README.md
README.md
test-project document.



BSD-3

This is an apple.

README.mdを見ると、変数名が埋め込まれています。(test-project)
また、create_documentの条件が"n"となっている箇所が表示されていないことを確認できます。
更に、条件式が"y"の時に表示される文章を確認できました。

最後に

cookiecutterを使うと、基本となる雛形から
オリジナルのプロジェクトを簡単に作成できます。

ちょっとだけ変えたプロジェクトを作成したいことは結構あります。
意外に応用できそうなので、もっと使ってみたいと感じています。