pythonでテキスト分類の復習

scikitlearnでテキスト分類を復習です。

KerasでのDeep Learning結果との精度比較を行うためにもベース知識として、個人的な備忘録です。

内容

scikit-learnにもともと付属している 20 news groupデータセットを読み込み、各種手法で分類するサンプルです。

ソースコードの大部分は、Classification of text documents using sparse features — scikit-learn 0.18.1 documentationがほとんどで、
一部、scikit-learn v18 に合わせてパラメタの名前を調整しています。

実行するとき、"--all_categories"を指定すると、KNeighborsClassifierが落ちてしまう(私のメモリ1Gという心もとない環境のためと思います)ので、ひとまずコメントアウトしています。

grid searchがまだ仕上がっていないのでちょっと待ってね。

全体

#!/usr/local/bin/python2.7
# encoding: utf-8
'''
Created on 2017/02/03

based on http://scikit-learn.org/stable/auto_examples/text/document_classification_20newsgroups.html

@author: mzi
'''
from __future__ import print_function

import sys
import os

import logging
import numpy as np
from optparse import OptionParser
from time import time
import matplotlib.pyplot as plt
import gc

from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import HashingVectorizer
from sklearn.feature_selection import SelectKBest, chi2
from sklearn.linear_model import RidgeClassifier
from sklearn.pipeline import Pipeline
from sklearn.svm import LinearSVC
from sklearn.linear_model import SGDClassifier
from sklearn.linear_model import Perceptron
from sklearn.linear_model import PassiveAggressiveClassifier
from sklearn.naive_bayes import BernoulliNB, MultinomialNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.neighbors import NearestCentroid
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LassoCV
from sklearn.feature_selection import SelectFromModel
from sklearn.model_selection import GridSearchCV
from sklearn.utils.extmath import density
from sklearn import metrics

def size_mb(docs):
    return sum(len(s.encode('utf-8')) for s in docs) / 1e6

def trim(s):
    """Trim string to fit on terminal (assuming 80-column display)"""
    return s if len(s) <= 80 else s[:77] + "..."

def benchmark(clf):
    print('_' * 80)
    print("Training: ")
    print(clf)
    t0 = time()
    clf.fit(X_train, y_train)
    train_time = time() - t0
    print("train time: %0.3fs" % train_time)

    t0 = time()
    pred = clf.predict(X_test)
    test_time = time() - t0
    print("test time:  %0.3fs" % test_time)

    score = metrics.accuracy_score(y_test, pred)
    print("accuracy:   %0.3f" % score)

    if hasattr(clf, 'coef_'):
        print("dimensionality: %d" % clf.coef_.shape[1])
        print("density: %f" % density(clf.coef_))

        if opts.print_top10 and feature_names is not None:
            print("top 10 keywords per class:")
            for i, label in enumerate(target_names):
                top10 = np.argsort(clf.coef_[i])[-10:]
                print(trim("%s: %s" % (label, " ".join(feature_names[top10]))))
        print()

    if opts.print_report:
        print("classification report:")
        print(metrics.classification_report(y_test, pred,
                                            target_names=target_names))

    if opts.print_cm:
        print("confusion matrix:")
        print(metrics.confusion_matrix(y_test, pred))

    print()
    clf_descr = str(clf).split('(')[0]
    return clf_descr, score, train_time, test_time

if __name__ == "__main__":

    # Display progress logs on stdout
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s %(levelname)s %(message)s')

    # parse commandline arguments
    op = OptionParser()
    op.add_option("--report",
                  action="store_true", dest="print_report",
                  help="Print a detailed classification report.")
    op.add_option("--chi2_select",
                  action="store", type="int", dest="select_chi2",
                  help="Select some number of features using a chi-squared test")
    op.add_option("--confusion_matrix",
                  action="store_true", dest="print_cm",
                  help="Print the confusion matrix.")
    op.add_option("--top10",
                  action="store_true", dest="print_top10",
                  help="Print ten most discriminative terms per class"
                       " for every classifier.")
    op.add_option("--all_categories",
                  action="store_true", dest="all_categories",
                  help="Whether to use all categories or not.")
    op.add_option("--use_hashing",
                  action="store_true",
                  help="Use a hashing vectorizer.")
    op.add_option("--n_features",
                  action="store", type=int, default=2 ** 16,
                  help="n_features when using the hashing vectorizer.")
    op.add_option("--filtered",
                  action="store_true",
                  help="Remove newsgroup information that is easily overfit: "
                       "headers, signatures, and quoting.")

    (opts, args) = op.parse_args()
    if len(args) > 0:
        op.error("this script takes no arguments.")
        sys.exit(1)
    
    print(__doc__)
    op.print_help()
    print()

    #Categories
    if opts.all_categories:
        categories = None
    else:
        categories = [
            'alt.atheism',
            'talk.religion.misc',
            'comp.graphics',
            'sci.space',
        ]
    
    #Remove headers
    if opts.filtered:
        remove = ('headers', 'footers', 'quotes')
    else:
        remove = ()
    
    print("Loading 20 newsgroups dataset for categories:")
    print(categories if categories else "all")
    
    data_train = fetch_20newsgroups(subset='train', categories=categories,
                                    shuffle=True, random_state=42,
                                    remove=remove)
    
    data_test = fetch_20newsgroups(subset='test', categories=categories,
                                   shuffle=True, random_state=42,
                                   remove=remove)
    print('data loaded')

    # order of labels in `target_names` can be different from `categories`
    target_names = data_train.target_names

    data_train_size_mb = size_mb(data_train.data)
    data_test_size_mb = size_mb(data_test.data)
    print("%d documents - %0.3fMB (training set)" % (
        len(data_train.data), data_train_size_mb))
    print("%d documents - %0.3fMB (test set)" % (
        len(data_test.data), data_test_size_mb))
    print()

    # split a training set and a test set
    y_train, y_test = data_train.target, data_test.target
    
    print("Extracting features from the training data using a sparse vectorizer")
    t0 = time()
    if opts.use_hashing:
        vectorizer = HashingVectorizer(stop_words='english', non_negative=True,
                                       n_features=opts.n_features)
        X_train = vectorizer.transform(data_train.data)
    else:
        vectorizer = TfidfVectorizer(sublinear_tf=True, max_df=0.5,
                                     stop_words='english')
        X_train = vectorizer.fit_transform(data_train.data)
    duration = time() - t0
    print("done in %fs at %0.3fMB/s" % (duration, data_train_size_mb / duration))
    print("n_samples: %d, n_features: %d" % X_train.shape)
    print()

    print("Extracting features from the test data using the same vectorizer")
    t0 = time()
    X_test = vectorizer.transform(data_test.data)
    duration = time() - t0
    print("done in %fs at %0.3fMB/s" % (duration, data_test_size_mb / duration))
    print("n_samples: %d, n_features: %d" % X_test.shape)
    print()

    # mapping from integer feature name to original token string
    if opts.use_hashing:
        feature_names = None
    else:
        feature_names = vectorizer.get_feature_names()

    # Extract features by chi2
    if opts.select_chi2:
        print("Extracting %d best features by a chi-squared test" %
              opts.select_chi2)
        t0 = time()
        ch2 = SelectKBest(chi2, k=opts.select_chi2)
        X_train = ch2.fit_transform(X_train, y_train)
        X_test = ch2.transform(X_test)
        if feature_names:
            # keep selected feature names
            feature_names = [feature_names[i] for i
                             in ch2.get_support(indices=True)]
        print("done in %fs" % (time() - t0))
        print()
    
    if feature_names:
        feature_names = np.asarray(feature_names)
    
    # Classification
    results = []
    for clf, name in (
            (RidgeClassifier(tol=1e-2, solver="sag"), "Ridge Classifier"),
            (Perceptron(n_iter=50), "Perceptron"),
            (PassiveAggressiveClassifier(n_iter=50), "Passive-Aggressive"),
            #(KNeighborsClassifier(n_neighbors=10), "kNN"),
            (RandomForestClassifier(n_estimators=100), "Random forest")):
        print('=' * 80)
        print(name)
        results.append(benchmark(clf))
    
    for penalty in ["l2", "l1"]:
        print('=' * 80)
        print("%s penalty" % penalty.upper())
        # Train Liblinear model
        results.append(benchmark(LinearSVC(loss='squared_hinge', penalty=penalty,
                                                dual=False, tol=1e-3)))
    
        # Train SGD model
        results.append(benchmark(SGDClassifier(alpha=.0001, n_iter=50,
                                               penalty=penalty)))
    
    # Train SGD with Elastic Net penalty
    print('=' * 80)
    print("Elastic-Net penalty")
    results.append(benchmark(SGDClassifier(alpha=.0001, n_iter=50,
                                           penalty="elasticnet")))
    
    # Train NearestCentroid without threshold
    print('=' * 80)
    print("NearestCentroid (aka Rocchio classifier)")
    results.append(benchmark(NearestCentroid()))
    
    # Train sparse Naive Bayes classifiers
    print('=' * 80)
    print("Naive Bayes")
    results.append(benchmark(MultinomialNB(alpha=.01)))
    results.append(benchmark(BernoulliNB(alpha=.01)))
    
    print('=' * 80)
    print("LinearSVC with L1-based feature selection")
    # The smaller C, the stronger the regularization.
    # The more regularization, the more sparsity.
    clf = LinearSVC(penalty="l1", dual=False, tol=1e-3)
    results.append(benchmark(Pipeline([
      ('feature_selection', SelectFromModel(clf)),
      ('classification', LinearSVC())
    ])))
    
    # make some plots
    indices = np.arange(len(results))
    
    results = [[x[i] for x in results] for i in range(4)]
    
    clf_names, score, training_time, test_time = results
    training_time = np.array(training_time) / np.max(training_time)
    test_time = np.array(test_time) / np.max(test_time)
    
    plt.figure(figsize=(12, 8))
    plt.title("Score")
    plt.barh(indices, score, .2, label="score", color='navy')
    plt.barh(indices + .3, training_time, .2, label="training time",
             color='c')
    plt.barh(indices + .6, test_time, .2, label="test time", color='darkorange')
    plt.yticks(())
    plt.legend(loc='best')
    plt.subplots_adjust(left=.25)
    plt.subplots_adjust(top=.95)
    plt.subplots_adjust(bottom=.05)
    
    for i, c in zip(indices, clf_names):
        plt.text(-.3, i, c)
    
    plt.show()

    

今年の事始めはkerasから

Deep Learningが流行していますね。流行している理由は2つ。

  • 研究的に良い精度が出る報告がされていること
  • 使えるSDKなどの基盤がそろってきたこと

これらが相まって、以下のような好循環が生まれています。

  1. 研究者が良い報告がされた論文を発表する
  2. 誰か(または本人)がSDKを使って実装し公開する
  3. これを使って試して自分のタスクに適用してみる
  4. うまくいかなかったところを改善する
  5. 論文にして発表する

まだまだ、研究の領域を出ていないとは思いますが、こんな感じで好循環が生まれています。
この辺の話はまた別の機会にでも。

で、いろんな基盤が出てきてます。
ざっくりまとめるとこんな感じ。まだまだたくさんありますが、最近はこんなところに集約されてる気がする(日本では)。

行列計算ライブラリ Tensorflow(by Google), Theano(by モントリオール大学)
SDK Chainer(by Preferred Networks, inc.), Keras(by Googleになるかも), The Microsoft Cognitive Toolkit(旧CNTK by Microsoft)

この他にもありますので"deep learning フレームワーク"で検索をかけてみてください。また、Wikipediaにもこんなエントリがあります。
Comparison of deep learning software - Wikipedia

よく言われる TensorFrowは分散処理に優れた行列計算ライブラリであって、実際はDeepLearning専用ではありません。なので、高次元のSDKがほしくなります。
高次元のSDKとして有名なのは、Chainer。日本人が主に作っているので日本語ドキュメントも充実していてとっつきやすいです。

Kerasってなに

ひとことで言うと、行列計算ライブラリを使いやすい形で提供する高次APIです。

バックエンドの行列計算ライブラリは、TensorflowとTheanoです。これらを切り替えて使うことができ、これぞれの分散処理の恩恵やGPUを利用した高速化の恩恵を受けることができます。
日本語ドキュメントも作られています。

Keras Documentation

始めるまでのおすすめの手順

個人的な感想ですが、Kerasは、実際使ってみると、Chainerに似ていてプログラマはとっつきやすいです。しかし、DeepLearningの基本構成を知らないと訳が分からないとは思います。ですので、おすすめ手順は以下。

ニューラルネットの基本を知る

まずは、以下を読んで、ニューラルネットの基本をPythonの実装として体感しましょう。でも、いきなり数式から入るとめっちゃ難しくなってしまいます。そこで、以下の本がおすすめです。

  • 実例を含めてPythonソースコードがかいてある。
  • 数式も載っているけど、微分とか行列演算から書いてあるので、高校数学の知識がなくても最悪読めてしまう。
  • また、実装形態も、KerasやChainerが目指している高次APIの方法に近い。

DeepLearningもちょっとかじりますが、これ以上難しくなると実装は大変な領域に入ります。ちょうどいいところで止めてあるのも、この本のよいところで、わかった気にさせてくれます。聞きかじりで手を出せてしまう危険さはありますが、、、。

わかってる人も復習もかねて見ておくと勉強になります。知ってる人なら1weekあれば読み終わってしまえます。

DeepLearningでできることを知る

次に読むか、並行して読むとよいなと思ったのはこれ。ニューラルネットの理解を進めてできることは何かを知っておくと、モチベーションにつながります。

  • DeepLearingでできることの紹介
  • それぞれのいろんな実装形態の紹介
  • これらを使ったサンプルプログラムの紹介

新しい事例も交えて書いてあります。半面、少し時間がたつと、過去の事例になってしまうかもしれないので、読むなら今です。

サンプルプログラムも載っているので、ノードやバックプロパゲーションの実装の方法や、図の表示方法なども書いてあり、役立ちます。

この辺まで大体ザクッと読んでみてからだと、すごく理解が早いと思います。

Kerasをインストール

ニューラルネットやDeepLearningの基本が大体わかったら、Kerasを触ってみましょう。

インストールですが、ドキュメント通りで大体終わります。

Keras Documentation

Ubuntu 14.04LTSの環境で少し詰まったのは、numpy, scipy, pyyaml。numpy, scipy にはBLATが必要なので、pipで入れるよりも apt-get で入れてしまったほうが、依存関係がはっきりしてよいでしょう。また、pyyamlもlibyamlが必要なので、apt-getで。同じくh5pyも。

sudo apt-get install python-numpy python-scipy python-yaml python-h5py

cuDNNは今回入れませんでした。

また、バックエンドにTensolFlowを使いたかったので、Tensolflowのみ入れてみました。

Download and Setup

pipでインストール完了。

サンプルを実行してみる

ここで、Kerasのサンプルがexampleディレクトリにいくつかあります。

keras/examples at master · fchollet/keras · GitHub

わたしは、テキストデータ処理に興味があったので、とりあえず、動くかどうかだけ見てみようと思い以下を試してみます。

reuters_mlp.py Trains and evaluate a simple MLP on the Reuters newswire topic classification task.

タスクとしては、ニュース記事を46カテゴリに分類に分類するタスクですね。

python reuters_mlp.py
Using TensorFlow backend.
Loading data...
8982 train sequences
2246 test sequences
46 classes
Vectorizing sequence data...
X_train shape: (8982, 1000)
X_test shape: (2246, 1000)
Convert class vector to binary class matrix (for use with categorical_crossentropy)
Y_train shape: (8982, 46)
Y_test shape: (2246, 46)
Building model...
Train on 8083 samples, validate on 899 samples
Epoch 1/5
8083/8083 [==============================] - 5s - loss: 1.4141 - acc: 0.6819 - val_loss: 1.0555 - val_acc: 0.7686
Epoch 2/5
8083/8083 [==============================] - 4s - loss: 0.7821 - acc: 0.8175 - val_loss: 0.9347 - val_acc: 0.7942
Epoch 3/5
8083/8083 [==============================] - 4s - loss: 0.5468 - acc: 0.8644 - val_loss: 0.8641 - val_acc: 0.8109
Epoch 4/5
8083/8083 [==============================] - 4s - loss: 0.4141 - acc: 0.8978 - val_loss: 0.8620 - val_acc: 0.8120
Epoch 5/5
8083/8083 [==============================] - 4s - loss: 0.3244 - acc: 0.9180 - val_loss: 0.8896 - val_acc: 0.8120
2208/2246 [============================>.] - ETA: 0sTest score: 0.86964678913
Test accuracy: 0.793855743544

まあ、精度は感覚的にはこんなもんなんかな。SVMと比較してみたくなりますね。あとでやってみます。


そして、めっちゃソースコードがシンプル
ドキュメントを読んでみると、このデータは、データセットが既に用意されていて、簡単にデータをロードできるようになっている。そして、単語区切りをしてベクトル作成するクラスも実装されてる。この辺を参考にしながら、日本語版を作ればよさそうですね。

とりあえず、今日はこの辺で終了。

はじめてのブルベ完走!

もうかなりたってしまいましたが、BRM200を完走できました。

メダルももちろんもらいました。

f:id:mzi:20150718161425j:plain

完走したのはこれ。

AJ群馬 Audax Randonneurs Gunma: BRM517群馬200km 初めてのブルベ

BRM517群馬200は初心者向け

ブルベ名にもあるように、「初めてのブルベ」なので、非常に走りやすかったです。
主催者の皆さんに感謝です。

  • ほとんど平地を走る。峠は一か所だけ、今考えると、そんなにきつくなかった。
  • PC(チェックポイント)も無人で、コンビニ2か所と写真撮影だけ。
  • 走る道はだいたい幹線道路の近くだったり有名なサイクリングロード利用なので、トラブルがあっても自己解決しやすいし、コンビニもたくさんある。

平地ばかりだからかわかりませんが、結構広い範囲を走った満足感

私ははじめてブルベに参加したので、ほかのブルベは知りませんが、200kmでこんなにたくさんの県をまたぐとは思いませんでした。一部しか走っていいない県ももちろんありますが、初心者的には満足感ありでした。

群馬県→栃木県→茨城県→千葉県→埼玉県→群馬県

完走ルート
connect.garmin.com

ほとんど利根川沿いでしたが、流域面積日本最大がわかるこれだけの県をまがって走るのもなかなかないでしょう。

坂東大橋道標にて撮影。わかりにくい!

f:id:mzi:20150517180332j:plain

スタートとゴールの温泉もよし

体力を回復させてくれたのは温泉でした。
よしおか温泉で汗を流し、ゆっくりつかって、足や腕をゆっくりマッサージ、背中などをストレッチ。十分回復できました。

走行中の会話も楽しめました。

途中、利根川自転車道で、ロードバイクに乗ったおじいちゃんと会話。あちらから声をかけていただきました。まあ、真昼間から反射ベストを着て走っている変な人で目立ちますからね。

ほとんど平地を200Km走っていることを話すと、
「ずっと平地なの?それじゃつまらないでしょ。」とのこと。

この方、定年後に自転車をはじめ、現在70才、ほぼ毎日走っているとのこと。山が好きでよく峠越えに行ったり、レースを見に行ったりするそうです。

山登りのコツはなんですかと聞くと、
引き足を使うことかなと、「あと、あきらめないこと」とのこと。
奥が深い。

というわけで、一般の自転車道を走るので、こんな会話もできます。

個人的な良かった点と反省点

荷物系とコックピットは満足

初参加だったので、どんな感じにしたら良いのかわからず、いろいろ調べてきました。

荷物は、トップチューブバックはROSWHEELのMサイズ、サドルバックはORTLIEBのLサイズ。

ROSWHEELのトップチューブバックは、平地走行ではぎりぎりで足に当たらないサイズ。快適でした。しかし、山道でダンシングするときさすがにあたります。私はシッティングで登っていましたので、ほとんど気にならなかったですが、ダンシングの方はやめたほうがいいでしょう。

ORTLIEBのバックは、見た目のわりにかなり入ると噂で買いました。その通りで、着替えや雨具までいれても大丈夫。重さで走行時に自転車が左右に振られるのではと思ってましたが、そんなに高速走行するわけでもないので、気になりませんでした。

ORTLIEB(オルトリーブ) サドルバッグ L スレート

ORTLIEB(オルトリーブ) サドルバッグ L スレート

コックピットは、全部百均で揃えました。

  • B5のバインダー
  • ヘアゴム
  • 大型クリップ

これだけ。

B5のバインダーをステムに取り付ける。そのために、バインダーに穴をあけ、ヘアゴムを通し、ステムに縛り付ける。初めは、結束バンドも考えたのですが、ステムはねじ穴などで微妙に凹凸があるので、きっちり括り付けるとバインダーが曲がるし、緩いとがたがたする。そこで、ゴムで付けることにしました。あとはこれに、コマ図を挟んで押しまい。

f:id:mzi:20150509085126j:plain

難点は、目線が手元に行ってしまうので、走行が危うい時がある点。向きを横に取り付けるか、コマ図の作り方を工夫してもう少し上につけるのがよいかな。

日焼けしまくり

5月の日差しをなめてました。

日焼け止め系のグッズを何も持たず出発。上はインナーを着ていましたが、下はサイクリングパンツのみで走ってました。

見事に焼けて、昼ごろには、膝頭が真っ赤。
しかたないので、寒さ対策のために持って行ったタイツを履き、時々上から水をかけながら走りました。これでなんとかしのぎました。

春でも日焼け対策は必須ですね。

着替えはかなり良かった

パッド付きのパンツの変えを持っていきました。本当は荷物になるので必要ないと思っていたのですが、雨が降ったりしたとき取り換えがあったほうがいいと考えていたためです。

実際は別の使い方でした。

200Kmは初めてだったので、100 - 150km でかなり疲れていたし、おまけに、結構向かい風区間が多くて心が折れそうでした。で、リフレッシュするにはどうするか考えていました。着替えがあることに気づき、途中のコンビニでレーパンだけ着替えました。これ、かなりリフレッシュできすごく良かったです。

まとめ

というわけで、はじめてのブルベを完走できました。

次は300走るかな。もう一度200を走っておいて長距離に慣れておくかな。迷い中。

Garmin Edge 500 コース作成と転送の方法

私のサイコンはGarmin500。こいつのコース機能は結構使えることは、わかってきました。
コースで使いたい機能は二つ。

  1. ルートを表示すること
  2. ポイント情報(ウェイポイント)を表示すること

あとは、コースの作成と転送方法です。

作成と転送の方法の候補

作成方法の候補

  • Bike Route Toasterで作成する → ◎
  • Garmin Training Centerで作成する → △:マップが貧弱で作成しにくい
  • Yahoo ルートラボで作成する → ×:ウェイポイント作成できず
  • Garmin Connectで作成する → ×:ウェイポイント作成できず

YahooルートラボGarmin Connectは、ウェイポイントを作成できない。
でも、このサイトは、たくさんのルート情報があるので、非常に参考になる。これは何とか使いたい。

私にとっての選択肢は、Garmin Training CenterとBike Route Toaster。

Training Centerの使いにくさはものすごいです。地図が貧弱だし、マウス操作が非常によくない。

Bike Route Toasterは、さまざまな地図を切り替えながら見ることができ、かなりよい。

転送方法の候補

  • Garmin Training Centerで転送する → ○:すごく遅い
  • Bike Route Toasterで転送する → △:うまくいかないこともある
  • Garmin Connect で転送する → ×:ウェイポイントを転送できない
  • TCXファイルをGarminEdgeにコピーする → ?:試してない

Gamin Connectで転送するのは、結局ウェイポイントを転送できないので私の目的には合わない。

いろんな記事で、Yahooルートラボで作成しTCXファイルをGarminEdge500にコピーして転送する方法でうまくいっていない人の記事を見ると、ウェイポイントの数が問題らしい。これって結構問題なのではないかなぁ。

実際一番簡単なのは、先のBike Rote Toasterで転送。
これでうまくいかなかったら、Garmin Training Centerで転送するしかない。

Bike route toasterで作成と転送するのがベスト。実際にどうやる。

BRM517群馬200のルートの作成方法で説明します。

AJ群馬 Audax Randonneurs Gunma: BRM517群馬200km 初めてのブルベ

Yahooルートラボで参考になりそうなルートを探す

ブルベやロングライドイベントでは、ルートラボでルートを公開しているものがあるので、これを参考にするとよいはず。

今回のブルベでは、主催者が公開したものはないので、参考になりそうなルートを検索します。昨年度も同じタイトルでブルベをやっているので、きっと去年走った人がルートを公開しているはず。地図で検索だとなぜかうまくいかないので、名前で検索してみる。

ありました。

ルートを見る - ルートラボ - LatLongLab

たぶん去年のやつですね。先人に敬意をはらいつつ、参考にさせていただきます。
ここからGPXでダウンロードします。

Bike Route Toaster でインポート

Bike Route Toaster - Welcome to BikeRouteToaster.com

ユーザを作成しなくても使うことはできます。ですが、お勧めはユーザを作っておくことです。データをアカウントに保存できるので、作業途中のものを置いておけます。

上の[Editor]を押すと編集画面になります

インポート後こんな感じになります。
f:id:mzi:20150613210050p:plain

Bike Route Toaster で編集する

編集は、キューシートやコマ図を見ながらウェイポイントをチェックという地道な編集作業になります。これ結構時間がかかります。全く走ったことがない土地なので。私の場合頑張って一日つぶすぐらいかかりました。

編集方法を簡単に説明します。以下が基本です。もっと便利な機能もありそうですが、基本これだけ。

自動でルート設定を消す

今回はすでにルートがあることを前提に編集するので、右の「Routing」のAuto Routing のチェックを外す。

これをやっておかないと、面倒です。

アカウントに保存する

上の[Save]を押す。名前を付けてOKを押す。

アカウントに保存したコースを呼ぶ

上の[Load] を押す。保存した名前のコースを選択する

ポイントを移動する。

ルートの上にマウスを置くと「×」が出てくるので、ドラッグして移動。

ポイントを削除する。

ルートの上にマウスを置くと「×」が出てくるので、クリックして選択。左側ペインの「Editing」の中のPointのDeleteをクリック。

ポイントを追加する。

追加したいポイントの直前のポイント「x」を選択。「Editing」の中のInsertセクションのAfter Selected Pointを選択。追加したいポイントをクリック。

操作をやり直す

あわてずに 「Editing」のUndoを押す。ポイント追加と移動と削除が取り消されます。下記のコマ図などの情報の追加は取り消されません。

ポイントにコマ図やキューシートの指示を書き込む

書き込みたいポイント「x」を選択。左側ペインの「Editing」の中の「Point」セクションの中からPoint typeを変更します。

f:id:mzi:20150621070645p:plain

LeftやRightなどを選択します。これと同じものがGarminでも表示されます。非常にわかりやすい。
私は、Nameのところに、キューシートのポイント番号とLやRを書き込みます。

さらに、地図表示している上の[Cue Sheet]を選択すると、これらの情報をもとにキューシートが表示される親切設計。配布されているキューシートと比較しながらポイントが正しいか確認できます。

自分でルートを作成したいとき

いろいろやってみましたが、オス勧めは以下の設定。

地図を Open Cycle Mapに変更。Routing の Auto Routingにチェック、Route TypeをBike、Favor Bike Path を 1、Avoid High Wayのチェックを外す。

地図がGoogleだと。自動的なルーティングがうまくいかない。Avoid High Wayにチェックを入れると、国道すら通らなくなるので、変なルートになる。

Bike Route Toaster でデータをGarminに転送する

終わるとこんな感じになる。

f:id:mzi:20150621070905p:plain

上の[Export]を押すといろんな形式(TCX,GPX,KML,CSV,XML)のファイル保存などのメニューが出てくる。
このうち最後の Garmin を選ぶ。

ここで、実は事前準備がいる。GCP - Garmin Communicatorプラグインが必要。
あらかじめインストールしておく。

これで終了

柏~筑波サイクリング 小貝川に沿って

こちらもだいぶ時間が経過してしまいましたが、4月12日に千葉サイクリング協会主催のサイクリングイベントに参加しました。

www.chiba-cycling.org

千葉県柏市から茨城県つくば市までの往復120kmを走りました。
柏市つくば市も、私にとってそれなりに縁のある土地。つくば市は6年間ぐらい住んでいました。

また、初めてのサイクリングイベント参加&初めての集団走行体験でした。
募集定員300人ということで、どのように300人もの人数が走るのか想像がつきませんでした。
しかし、実際に走ってみるとスタート時間を少しずつずらし、20人くらいのグループを作って走行するようになってました。この人数でも結構走れるもんなんですね。

もう二つ試したいことが、それは、Garmin500のコース機能を試すこと、あとコマ図を見るための装備を試すこと。これらは、かなりいい感じでした。最後にレポートします。

スタート地点 柏の葉キャンパス駅

いわゆるウェーブスタートというやつで、すこしづつスタート、大体10グループぐらいでしょうか?私は遅いので、一番最後のグループに参加。

そして堤防。菜の花万満開。

f:id:mzi:20150412083956j:plain

途中のエイドステーションで、アンパンとクリームパンをもらいました。
さすがに最終グループでは少し遅かったので、ちょっと先行させていただきました。

折り返し地点 つくば市北条の平沢官衙遺跡(ひらさわかんがいせき)

ここでお昼ご飯をいただきました。お弁当とお茶とをいただきゆっくり休養をとり再スタート

今回は第3グループぐらいに参加。かなり早めにスタートしました。
ここでハプニング。船頭いただいた方がコースミスで、5kmぐらいボーナスステージをこなしました。これも集団走行の醍醐味ですね。

帰りのエイドステーションでもアンパンをいただきました。

100km越えたあたりで足がとまってしまいました。ここで、個人的に休養。控えでもっていたスポーツゼリーを飲む。基礎体力をつけないとだめだな、やっぱ。

ゴール地点も柏の葉キャンパス駅

やっと到着しました。まあまあまだいけそうな感じ。

f:id:mzi:20150412151608j:plain

まとめ

connect.garmin.com

120kmで、休憩込で7時間くらい。目標タイムぎりぎりでした。
休憩のとり方は結構大事。無理せず休む。自転車の上で休めればいいんだけどなぁ、、、。

Garmin500のコース機能は使える

地図はないので、画面にコースの線しか出てきませんが、結構使えることがわかりました。
どっち方向に行くか、道から外れたかはわかります。
コマ図に出ているポイントを登録しておくと、そこまでの距離も出てくるので、疲れているときには、少しずつ進んでいくのがわかりよい感じです。

コマ図を見る方法

今回、初めてコマ図を見るための装備を導入しました。装備といっても100円ショップで買った、A5サイズのバインダー、ヘアバンドゴム、透明ビニールポーチを組み合わせたもの。

A5バインダに穴をあけ、ヘアゴムでハンドルとステムの上に括り付けるだけ。
コマ図の紙をビニールポーチに入れてA5バインダに挟む。

コマ図の切り替えは、手動でやるので、少し手間ではありますが、休憩するときにやれば全然問題なし。

難点は、目線が下に向くこと。紙の印刷方法を工夫してみることにしようかな。

荒川サイクリングロードの一筆書き

去る3月28日に荒川CR一筆書きを完了しました。
かなり記事を書くのがおくれてしまいました。
いまさらですが、ひとまず、顛末を書こうと思います。
この期間、CA千葉主催の柏つくば往復イベントなどにも参加しています。
そちらの記事も近々に書きます。

荒川サイクリングロードってどこまでなの?

荒川CRと一言で言っても、実は明確な定義はないんです。

荒川サイクリングロード[3][4]は正式名称ではなく、埼玉県道155号さいたま武蔵丘陵森林公園自転車道線[5]および沿岸自治体によってサイクリングコースとして整備されている道(基本的に自転車歩行者専用道路となっている)および、それに類する道路(河川区域内の河川管理用道路や生活道路など)を総称して呼ばれているため、正式な起点・終点はない。

荒川サイクリングロード - Wikipedia

Wikipedia記載のように、河口~戸田橋~羽根倉橋~入間大橋~熊谷大橋~武蔵丘陵森林公園ということにしてみました。

サイクリングロードマップ - 埼玉県

今回のコース

一筆書きなので、左岸と右岸とを組み合わせて一周します。

私は、江北橋付近に住んでいるので、以下のようなコースとしました。

右岸:江北橋→河口
→左岸へ:清砂大橋→秋ヶ瀬公園→榎本牧場→熊谷大橋
右岸へ:森林公園→吉見運動公園→上江橋→岩淵水門→江北橋


実際に走った履歴はこちら。

途中でGarmin500君のGPSがうまく取れていないようで、榎本牧場付近と熊谷付近がうまく取れてません。
ちょっと複雑なところがとれていないので、参考にしてもらう方には申し訳ないです。

なので、コースだけ編集しました。

約180 km。

江北橋から河口へ(右岸

ここは、ずっと自転車道が整備されていて一般道はなし、さらに、迷うような箇所もなくすごく走りやすい。

体力も全然残っているし、晴れて爽快でよい感じ。

河口には船着き場の標識があります。ここで日の出を迎えました。

f:id:mzi:20150328060004j:plain

河口から秋ヶ瀬公園へ(左岸)

少し一般道を通り、次の橋で中川へ。そこから自転車道がずーっと整備されている。
時々どこに行ったらいいか迷うところもありますが、まあ、なんとかなるでしょう。
私の場合、ほぼ毎週末走っているのであまり迷いなく行きました。

ここで大体60km。まだまだ体力残ってますね。順調順調。

秋ヶ瀬公園終点の羽根倉橋にて

f:id:mzi:20150328082734j:plain

秋ヶ瀬公園から榎本牧場(左岸)

少し一般道をとおるけど、全然車は通らないので、気にすることはほぼなし。

ここでだいたい70km。体力的にもまだまだ順調。ペダルもよく回る。
Garminが反応していなかった部分を加味すると実際は77kmぐらい。

土手を走るので、あまり景色の変化はないが、
そこに埋まっている菜の花はめっちゃきれい。
サイクリングロードで唯一ある路線の遮断機とともに。

f:id:mzi:20150328085207j:plain

そして、榎本牧場で休憩。
久しぶりに食べましたジェラート。疲れた体にしみる糖分最高です。オープンの5分前くらいにつきましたが、快く対応いただきました。ありがとうございました。

f:id:mzi:20150328092631j:plain

榎本牧場から熊谷大橋(左岸)

ここもずーっとサイクリングロード。
はじめの10kmくらいは一般の農道も利用してるのですが、ほとんど車の通行なし。
そしていつのまにか堤防にもどります。

ずんずん進みますがさすがに疲れてきました。
景色の変化がないのも疲れを助長させる、、、。

ついに荒川沿いの最終地点到着。
とういことにします。このさきは砂利道なので。
ここで100kmちょうど。

f:id:mzi:20150328113108j:plain

熊谷大橋から森林公園(左岸→右岸

お昼ご飯を食べに、少し市内に入ります。

お昼ご飯は、ここ。
グリーンズ (Greens) - 石原/カフェ [食べログ]

おいしいパスタセットをいただきました。
f:id:mzi:20150328115509j:plain

景色が一気に都会街中に変わる。雰囲気も変わるので、心機一転。
途中のaeonにもよって、おにぎりや飲み物を補充。

そして、森林公園へレッツゴー。
荒川沿いの堤防にはなかった山道を少しだけ体験。
そんなにきつくないはずなのだけど、この100kmを超えた地点ではかなりこたえる。

やっと到着、折り返し地点。

f:id:mzi:20150328134518j:plain

ここで、大体120kmくらい

森林公園から吉見運動公園(右岸

すこし市内の道を通り、また荒川の堤防沿いに戻ります。
そして、きれいな菜の花ロード。

f:id:mzi:20150508074936j:plain

でもだんだん飽きてきたかな、菜の花。

吉見運動公園でひとしきり休憩しました。ここは、サイクリストの憩いの場となっており、この日は、自転車ラックいっぱいでした。

子供用ロードバイクとお父さんで来ている方も見かけます。
小学2年生くらいでしょうか。こんな小さなころから乗っていて、自転車選手になったら、すごい選手になりそう。

吉見運動公園から上江橋(右岸

ここも、ほぼまっすぐな自転車道が整備されていて、走りやすい。でも、単調なのがたまにきず。
途中のさくら堤公園で桜みえるかなと期待してましたが、3末では全く咲いていない。かなり残念でした。

f:id:mzi:20150508075925j:plain

ホンダエアポートにもよらず、一気に上江橋に行きます。というのも、このあたりから、微妙に向かい風。足を止めると前に進めなくなりそうだったので、とにかく回すべしと思ったので、一気に行きました。

上江橋で少し休憩。おにぎりを食べる。

上江橋~岩淵水門(右岸

だんだん足が回らなくなっていきました。いつも通っている道も、足取り重く。スピードもでず。

仕方ないので、戸田競艇の対岸あたりでいったん休憩。
いつも早朝しか通らないのでほとんど誰もいないのですが、昼間は人通りも結構ありますね。野球の練習に行く高校生、それを見に来るおっちゃん、犬の散歩のおばちゃん、ジョギングしてるおねさん、もちろん、自転車乗りも。しばし、人間観察。

で再開し、何とか岩淵水門に到着。

f:id:mzi:20150508194305j:plain

ここの桜も1分咲きといったところでした。

岩淵水門から江北橋(右岸

ここもいつも通る道。なのに、いよいよだめそうです。土手を上がる足もない。
なんと、歩いて登りました。おはずかしい。

そして、なんとかゴールに到着。

f:id:mzi:20150508195121j:plain

Garminの表示は170km 7時間30分ぐらい。
実際には、177km 休憩込で14時間47分。
もう少し早くつきたかったですが、ロングライドの自身はつきました。

おえてみて

休憩込でも10時間くらいが理想でした(平均20km/h、昼食休憩1時間)。
そこまでいけず、時間はかかりすぎ。お昼休憩を2時間くらいとったのを削減しても、まだ理想にはいかないので、やはり基本は自転車に乗っている時間でのスピードを上げるしかない。特に後半で130kmを超えたあたり。

でも、ロングライドの経験にはなりました。このくらいの距離なら走れるとう自身も着きました。これで、ブルベ200もいける自信はついてきました。

というわけで、ブルベ200 にエントリしました。

参加イベントを決めてみる

今年は、ブルべに初参加することを目標にしている。

参加できそうなブルべを選定してみた。初心者は年始に動かないといけないですね。どこも2月までに初心者向けのBRMは終わっている、、、。年始は簡単で、年末にかけてだんだん難しくなっていくようにスケジューリングされていますね。早めに知っておけばよかったな。

BRM517群馬にエントリーしよう。希望者が多いかもしれないので、エントリーを頑張らないとねぇ。ダメだったら、BRM919 東京200、BRM 920東京200でいこうかな。

あとは、これに合わせて長距離イベントに参加しておきたいですね。
調べた中で、タイミングが合いあいそうなものは、柏~筑波サイクリング 小貝川に沿って 120キロ(千葉県 柏市・茨城県筑波市)かな。300名までエントリー可能で、120km走るって、ちょっと多すぎな気もする。集団走行初めてなのでここで練習しますか。ただ、もう少し距離がほしいかな。センチュリーライド的なものがあればよいのだけど、ちょうどいいタイミングでないんだなぁ。

参加できそうなブルべ

BRM517群馬200km 初めてのブルベ

いままでBRMに参加したり、認定を受けた経験が無い方、あるいは久しぶりにBRMを走ってみようと思った方にお勧め。サイクリングに最適な季節に AJ群馬が開催する定番コース。山あり、河あり、平地あり 色々な景色の中を走っているうちに気が付けば栃木・茨城を経由して千葉まで走ってしまうオトナ のプチ冒険 貴方も走ってみませんか?

ということで、いい感じです。

ルートはまだ発表されていませんが、去年のものは以下に参考があります。

BRM524 群馬200km 初めてのブルベ春 - ルートラボ - LatLongLab

最高標高は350mくらい。序盤50kmまで山登りがあり、後半は平坦。平坦しか走ったことのない私にとってはこれでもつらいかも。

BRM919 東京200 ツール・ド・ブルベ焼津

連続ブルベ7日間の旅、毎日200キロをサイクリング、景色、名所旧跡を楽しみながら旅をするブルベです。毎日の目的地は温泉地、汗を流した後には○○が待っている。初日焼津温泉、等々力から中原街道、熱海峠、富士、サッタ峠、久能街道、宇津ノ谷峠、焼津と平坦なコース。夕食は漁港の食堂かな?認定実施初回。

ルートは以下。2015BRM919東京200TB焼津 - ルートラボ - LatLongLab

海岸線をずっと走るので、平坦のまま進む部分が多い。なので、まあまあ初心者でもいけるかも。

BRM 920東京200 ツール・ド・ブルベ長島

連続ブルベ7日間の旅、2日目長島温泉、焼津から御前崎、新居の関所跡、豊橋蒲郡三河安城、長島とめぐり獲得標高約500mと超平坦なコースだが、名古屋近郊での渋滞峠とか。浜名湖蒲郡で旨い物が待っているかも? 認定実施初回。

ルートはこのさき2015BRM920東京200TB長島 - ルートラボ - LatLongLab

海岸線をずっと走るので、平坦のまま進む部分が多い。なので、まあまあ初心者でもいけるかも。