より良いエンジニアを目指して

1日1つ。良くなる!上手くなる!

第32回 Pythonもくもく会に参加してきました

mokupy.connpass.com

表参道のおしゃれなスペース。

主催者の方にはwifiも電源も完備でこのようなスペースを無料で提供していただいたことに大変感謝します。

雰囲気はシーンとしていて、本当に黙々。

その代わり、Slackで「どわーっ!」とやりとりしているのかと思いきや、そういうこともなく、黙々。

Slackでかき回してみようかと投稿してもシーン。

みなさん、自分の作業に真剣に取り組んでいる様子でした。

この勉強会に参加することになった経緯

こういう勉強会に参加すると、IT関連の仕事をしているわけでもないが、プログラミングを仕事に活かしたいなどと行った目的で参加される方々もいらっしゃいます。

そういった方と話すきっかけがあって、自分も新鮮な気持ちでプログラミングに向き合ってみようと。

とわけで、ほとんどやったことのないPythonの勉強会に参加することにしました。

今日の私のやること

普段はC#erで、機械学習C#でチャレンジして撃沈する私ですが、さすがにPythonの会なのでおとなしくPythonをやります。

www.shoeisha.co.jp

この本を入手したので、この本を片手に演習。

第2章 Pythonの基本

基礎は重要。左手は添えるだけとか思い出します。

が、こんなの一からやっていたら、いつまで経ってもたどり着けねーぜ。

と単純は四則演算は適当に進めます。

この本はNumpyでの行列の基礎も掘り下げてくれるのはいいですね。

In [63]:
x = np.array([1,1,2,3,5,8,13])
x > 3
Out[63]:
array([False, False, False, False,  True,  True,  True], dtype=bool)

これとかすごい。x > 3 の一発で判定して出力してくれる。

第3章 グラフの描画

グラフが簡単に描画できるので、楽しいパート。javaScriptよりもデスクトップアプリよりも簡単に描画できます。

f:id:rimever:20181028123925p:plain

こんな感じ。

In [7]:

# 3次関数 f(x) = (x-2)x(x+2) を描く
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
def f(x):
        return (x - 2) * x * (x + 2)
In [8]:

print(f(1))
-3
In [9]:

print(f(np.array([1,2,3])))
[-3  0 15]

こういう1つの関数で変数1つでも配列でも簡単に処理されるのは楽。

とはいえ、C#はそのためのLinqがありましたか。

4章 機械学習に必要な数学の基礎

お昼終わった後、他の方に私が作業している本も見ていただいたのですが、シグマなどの記号をきちんと説明してくれるのがいいとのこと。

ちょうどその箇所に突入していきます。

微分積分辛いです。よくわかったのか、わからないのか。完了。

5章 教師あり学習:回帰

いよいよ機械学習に突入。

少し触ったところで、これまで。

Jupyter Notebook

使っていて気付いたのがJupyter Notebookの使い勝手の高さ。

処理の途中で、結果を確認できる。実際に実装するときでも、デバッグしながら進められるということ。

ちょっとしたコードを書いて試したいというのにはうってつけですね。

学習にも開発にはうってつけのツールですね。

SlackBot

他の方がPythonでSlackBotを開発していると聞いて、面白そうと思ったので、これも今度やってみようかと思います。

ML.NET 0.6という大変動に飛び込んでは見たが、難破して打ち上げられてしまった

ML.NETが0.6にバージョンアップしました。

これにより、大幅にAPIが見直され、これまでのはLegacy名前空間に移行されてしまうという大変動ぶりには驚かされました。

blogs.msdn.microsoft.com

そして、自分の書いていたソースはすでにレガシーとなったことにがっくし。

using Microsoft.ML.Legacy;
using Microsoft.ML.Legacy.Models;
using Microsoft.ML.Legacy.Data;
using Microsoft.ML.Legacy.Transforms;
using Microsoft.ML.Legacy.Trainers;

日本語ドキュメントはまだ古いままですね。

ML.NET を使用してアヤメの花をクラスター化する (クラスタリング) | Microsoft Docs

気を取り直して、0.6の説明を見てみることにしました。

短いサンプルは下記でも紹介されています。

Let's 10分 機械学習

ML.NET Tutorial | Get started in 10 minutes

DataKind

以前はモデルとなるクラスを用意して、列ごとにプロパティを用意して、アトリビュートでカラム番号を宣言していました。

確かにこれはまだるっこしいですね。

が、読み込むとき、下記のようにDataKindを割り当てます。R4とは何のことやら?

new TextLoader.Column("SepalLength", DataKind.R4, 0),

が、何を選べばいいのか?

DataKind Enum (Microsoft.ML.Runtime.Data) | Microsoft Docs

説明なし...

Estimator

レーニングを行うにあたっても、

var pipeline = new TermEstimator(env, "Label", "Label")
                   .Append(new ConcatEstimator(env, "Features", "SepalLength", "SepalWidth", "PetalLength", "PetalWidth"))
                   .Append(new SdcaMultiClassTrainer(env, new SdcaMultiClassTrainer.Arguments()))
                   .Append(new KeyToValueEstimator(env, "PredictedLabel"));

TermEstimatorとは何なのか?

今回は数値から文字列でしたが、数値から数値にする場合はどうすればいいいのか。不明。

TermEstimatorのインテリセンスにはコメントなし。

最後に

全くためになる情報を提供できず申し訳ありません。

ASP.NET Coreという変革にチャレンジして、うまくいかなかったのを思い出します。

まだまだ、これからかなという気がしています。

それでもC#機械学習ができるということに期待はしています。

GitHubをやってみて

流行りに疎い私なので、GitHubを最近使い始めるようになりました。

使ってみての感想を。

GitHubの私の使い方

社用のために打ち上げる

PCを持ち帰ったりしないので、会社で使えるアイデアができたらGitHubにコミットして、会社でソースを回収するということをしています。

会社の諸々の規約で出来ないことが色々あった結果です。

さすがに見せられないものは公開リポジトリにも置けませんが。 

記念保管

今後見ないだろうけど、そのまま捨てるのもちょっとなあといったソースなども置いてます。

 

Forkwell Portfolio

Forkwell Portfolioではリポジトリを解析してくれます。

なかなか面白いサービスだと思います。

ASP.NETではデフォルトでjavascriptを詰めてくれるので、ソースの比率がjavaScriptが多いということに。

もっとC#の上位を目指したいですね。

portfolio.forkwell.com

  

SourceTree

Gitの運用については私はSourceTreeをお薦めします。

www.sourcetreeapp.com

Visual StudioにもGitを扱う機能はあり、それで済めばアプリを複数起動する必要もないののですがよく操作ミスをしていました。

SourceTreeだと分かりやすいので、こちらを使うようになりました。

MacでもWindowsでも使うアプリはこれぐらいなのではないかと思います。

ゆるふわ C# もくもく勉強会 #60 に参加してきました

weeyble-csharp.connpass.com

前回を振り返って、どうするか

初参加するも、Macを復旧してました。

復旧し、0を1にすることでき、めでたくスタートアップを果たしました。

今回は、合わせてインストールした、Rider(リーダー)を使って何かをやろうと。

自然言語処理100本ノック

今、注目しているのがOpenCvSharpだったのですが、MacではOpenCvSharpを動かすのは難しそうなので断念。

自然言語処理100本ノックを知り、C#でチャレンジするという無謀なことをしてました。

github.com

敢えて、C#にこだわっていこうと進んでいました。

C#にもML.NETという機械学習ライブラリがあるのでいけると信じて突き進みました。

終盤になって、本格的に機械学習となって、いや、これ無理ゲーじゃんという状況に陥っていました。

そして、そのまま放置していました。

ある日、こんな記事を見つけたのでした。

orizuru.io

FIFA18の選手データというのもなかなか興味深い(そのうちネタとして解析したい)ですが、それだけでなくLightGBMというアルゴリズムも聞いたことがありませんでした。

www.slideshare.net

多分わかっていないけど、ML.NETにもあったから使ってみることを今回の目標にしました。

そもそも使い方がわからない!

How do I use the newly added LightGBM classifier? · Issue #429 · dotnet/machinelearning · GitHub

Attempted to access a path that is not on the disk.

ディスク上にないファイルにアクセスしようとしました。という意味。

        private const string RootDir = @"..\..\..\..\Chapter08.Core";

        readonly string _dataSourceDirectoryPath =
            Path.GetFullPath(Path.Combine(RootDir, @"rt-polaritydata\rt-polaritydata"));

元々のソースはWindowsで開発していましたが、Macだと\はダメでしたね。¥マークは/に差し替えます。

        private const string RootDir = @"../../../../Chapter08.Core";

        readonly string _dataSourceDirectoryPath =
            Path.GetFullPath(Path.Combine(RootDir, @"rt-polaritydata/rt-polaritydata"));

System.InvalidOperationException : Entry point 'Trainers.LightGbmBinaryClassifier' not found

下記の記事で説明されています。英語はよく分からないですが、サイトの画像を見てなんとなく理解しました。

elbruno.com

Downloadしろよと。ML.NETだけでもインテリセンスが出てくるのですが。

NuGet Gallery | Microsoft.ML.LightGBM 0.5.0

NuGet Gallery | LightGBM 2.1.2.2

が、Entry pointの例外が解消されず・・・・・・。

ここで終了。

2時間で成果を上げるのは難しいですね。修行あるのみ。

追伸

LightGBM doesn't work during F5 of a .NET Core application · Issue #482 · dotnet/machinelearning · GitHub

ML.NET 6.0にアップグレードすれば回避できるかもしれません。

次回、試してみようかと思います。来週やることが決まりました。

WPFとWinFormsの起動比較

WPFでシステムを開発している時のこと

私「どうしても起動遅いですね」
メンバー「WPFだからじゃないですか」
私「どうしても処理が重くなってしまって」
リーダー「WPFだからね」

実際どんなものなのか。私なりに検証して見ることにしました。

※あくまでも参考でお願いします。

検証方法

使用ライブラリ

  • BenchmarkDotNet
  • FlaUI.UIA3

BenchmarkDotNetで計測を行います。 Processクラスを使うだけでもいいのですが、どうしてもウインドウが表示されてからを判定したいのでFlaUIという自動UIのライブラリを使用しています。

検証方法

何もない800,600のウインドウを表示するだけのアプリをWPF,WinFormsそれぞれに用意します。

f:id:rimever:20181024213939p:plain

本当にのっぺらぼうなウインドウです。

そして、下記のようなアプリを呼び出しては閉じを行う処理で、計測して比較してみます。

        public void Launch(string filePath)
        {
            var app = FlaUI.Core.Application.Launch(filePath);
            using (var automation = new UIA3Automation())
            {
                app.WaitWhileBusy();
                var window = app.GetMainWindow(automation);
                window.Close();
            }
        }

結果

BenchmarkDotNet=v0.11.1, OS=Windows 10.0.17134.345 (1803/April2018Update/Redstone4)
Intel Core i7-7920HQ CPU 3.10GHz (Kaby Lake), 1 CPU, 2 logical and 2 physical cores
  [Host]     : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0
  DefaultJob : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0

Method Mean Error StdDev
WPF 411.1 ms 11.159 ms 30.55 ms
WinForms 232.1 ms 4.893 ms 14.43 ms

この結果だけを見る限りでは、WinFormsの方が早いようです。

周囲が言っていたようにWPFは確かに重いかも。

終わりに

ただのウインドウを起動しては閉じるだけでした。

余裕があればボタンが10個配置したら? 実処理も実装したら? も試してみたいですね。

はてなブログやQiitaを使ってみて

勢いがある時でないと重い腰が上がらないから、Qiitaやはてなブログを使ってみました。

私の使った印象を紹介します。あくまでも個人の意見です。

Qiita

技術記事投稿サイト。

画像は貼り付けしやすいです。

ここに書いていければいいかなと思っていました。

が、下記の記事での意見を見て、Qiitaに投稿するのをためらってしまいました。

qiita.com

 

はてなブログ

結局、こちらに落ち着きそう。

Qiitaだと勉強会に行って来たとか書けないので。

はてなブログのいいところは、リンクをコピペすると自動的に、埋め込みか、リンクタイトルか選べるところです。

なので、ページへのリンクは非常に扱いやすいです。

画像の扱いについては、Qiitaの方がいいですね。ただし不便でもありません。

お世話になっている会社さんは技術ブログをやっているのですが、技術ブログは技術者の採用に役立つという説があると伺ったら、求人応募される方の中では技術ブログを見て、「技術に関心があるんですね」と来るそうです。

技術ブログ、想像以上に価値がありますね。

logmi.jp

私も月4件を目指して書きまくりたいです。

 

 

Confluence

少し領域は異なりますが、会社で使わせてもらっているので。

画像貼り付けは使いやすく、見やすいです。

プラグインは充実しています。

管理者にお願いして追加してもらったPlantUmlはお気に入りです。

PlantUmlで作成したシーケンス図などを表示できるので開発資料を作成しやすいです。

www.planttext.com

強いて言えば、はてなブログみたいにリンクのコピペの時に自動的にタイトルを取得して欲しい点。 

CommandLineParserを使ってみて

C# にはコマンドラインをパースするライブラリがあります。

github.com

コマンドラインパーサーなんて自分でも作れなくもないですが、

既存のライブラリがあるなら一から作ることもないので下記サイトを参考に試してみました。

qiita.com  

プロジェクトファイルのファイル追加漏れを防ぐコンソールアプリを作ってみようというときに使ってみたのですが、私がやりたかったことは……  

  • どうしてもプロセスの結果として返して失敗したか成功したかを判断できるようにしたいので、返り値に0かそうでないかを返す。
  • さすがに非同期で処理するまでもないかなと、async伝播させたくない。

ということで、下記みたいな形で私は実装してみました。

public class ProjectAssistantParser
{
    public static int Parse(string[] args)
    {
        using (var parser = new Parser(setting => setting.HelpWriter = null))
        {
            var parsed = parser.ParseArguments<CommandLineArgument>(args);
            bool isSuccess = true;
            parsed.WithNotParsed(er =>
            {
                    var helpText = HelpText.AutoBuild(parsed);
                    Console.WriteLine($"parse failed: {helpText}");
                    isSuccess = false;
            });
            parsed.WithParsed(
                p => { ProjectAssistantService.TryValidate(p.ProjectFileName, p.ChildDirectoryName); });

                return (isSuccess ? 0 : -1);
            }
    }

    /// <summary>
    /// コマンドライン引数を扱うクラスです。
    /// </summary>
    class CommandLineArgument
    {
        [Value(0, MetaName = "projectFileName", HelpText = "更新対象のプロジェクトファイル名",Required = true)]
        public string ProjectFileName { get; set; }

        [Value(1, MetaName = "ChildDirectoryName", HelpText = "ファイル走査対象とするサブディレクトリのフォルダ相対パス",Required = true)]
        public string ChildDirectoryName { get; set; }
    }
}

とりあえず、狙い通りにできたので良さそう。

テストコードとしても

        /// <summary>
        /// <see cref="ProjectAssistantParser.Parse"/>をテストします。
        /// </summary>
        /// <returns></returns>
        [TestCaseSource(nameof(ParseTestCaseSource))]
        public int Parse(IList<string> args)
        {
            return ProjectAssistantParser.Parse(args.ToArray());
        }

        /// <summary>
        /// <see cref="Parse"/>のテストケースです。
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<TestCaseData> ParseTestCaseSource()
        {
            string projectFileName = "hoge";
            string subDirectoryPath = "test";

            yield return new TestCaseData(new List<string> {projectFileName}).Returns(-1)
                .SetName($"{nameof(Parse)} 引数が足りない");
            yield return new TestCaseData(new List<string> {projectFileName, subDirectoryPath}).Returns(0)
                .SetName($"{nameof(Parse)} 正常系");
        }

とテスタブルに実装できますね。