modest violet

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

modest violet

開発者としてのあれこれや、日々の雑記など

your future hasn't written yet. no one's has.
by Emmett Lathrop "Doc" Brown

【de:code2017】「変わらない開発現場」を変えていくと決めた瞬間

今年もMicrosoft de:codeに参加しました!


de:code(デ・コード)とは

マイクロソフト テクノロジのビジョンと、「クラウド」「モバイル」を最大限に活かせる最新テクノロジをすべてのITエンジニアの皆様にご紹介するイベント

『変わらない開発現場』はツボ過ぎる

昨年度のde:codeでは直接セッションを受けていなかったので、今年は先陣切って日本マイクロソフト株式会社 赤間信幸さんのセッション『変わらない開発現場』を変えていくために〜エンプラ系レガシー SIer のための DevOps 再入門〜を受講しました。

昨年度の内容はこの記事に想いを込めています。
shin21.hatenablog.com

SIerに欠けている「技術を熟知したアーキテクチャー」

SIerから協力会社へ開発を依頼する際に、軸となるアーキテクトに関してはSIer側のプロパーが押さえておくべきであり、そこは右から左へ流すモノでは無いという事に関しては、全くの同意見です。
中でどういう仕組みで動いているかも分からない、ブラックボックス的な代物をそのままお客様へ提供する事例もありました。大抵は後々揉めています。

ただ、大抵はプロパー側で進捗管理はしても、内部の技術部分は「おまかせ!」っていう形も多い訳です。
何故ならば、「アーキテクチャー」というキャリアパスが準備されている企業はまだまだ少ないといえるからです。

SIerキャリアパスっていうと、大抵はプログラマーとして入社しても、仕様設計が出来るようになるとSEというポジションに昇格され、プログラムを組むという機会がだんだん少なくなります。じゃあプログラムは誰がするのか、というと外注業者さんに依頼して、管理側に回る(回らされる)事が多いです。現に私の会社でもそうです。

でも、中には最新技術を追いかけていたい、管理職なんて興味が無いという人たちもいます。そういった人たちの会社にいる居場所が少ない、というジレンマを取り上げられていました。
本当はその会社にいたいけれど、居場所が無い人たちは新天地を求めて会社を去ります。会社側も優秀な人員が離職し痛手を負います。双方不幸ですよね、と。悲しきすれ違いです。

継続的インテグレーションの目的は「ビルドの可視化」

継続的インテグレーションは自動ビルドを行い、自動デプロイする一連の自動処理です。
ビルドを自動化してどうするの?と言うことです。私自身もそう思っていました。

ビルドを自動化するのが目的では無く、そこから取得できる計数データや何件ビルドした、どれくらいソースを修正したという変更情報を取得し、それをグラフ化する事により、「自分の直感を客観的にとらえる」という事が目的である。

目から鱗とはこの事かと。真なる目的を知らずに目先の単語に惑わされていたようです。

近代的なプロマネは「サーバントリーダーシップ

上の人が下の人にあれこれ指示を出し、下手すれば奴隷のごとく扱うかつてのプロマネのイメージとは真逆で、下の人たちが働きやすいように上の人が色々と調整し、メンバーのやる気やモラールを引き出す事が重要である、という事です。

トップダウンがかつてのシステム開発におけるイメージですが、ボトムアップも上手く取り入れることで、マネージャーの管理工数の負荷を減らし、皆が幸せに働ける道筋を作るという事がプロマネに求められていると感じました。

さいごに

今の環境に嘆くのでは無く、自分で出来ることを考えて、小さな事からコツコツと変えていく必要があります。

今回記事の中で触れていませんが、他にも色々と考えさせられる内容でしたので、画像でご紹介しておきます。


Visual Studioを使用中に「SQL Serverは動作を停止しました」が頻繁に発生する場合の対処法

特定の環境でVisual Studio 2015 または 2017 を使用していると、「sqlservr.exeは動作を停止しました」というメッセージボックスが頻繁に発生します。

現に私もこの現象に悩まされていました。一回一回のメッセージ表示は何ともないのですが、定期的に絶えず表示されるため、イライラが募るわけです。精神衛生上宜しくない。

原因はSQL Server Local Db

Visual Studioインストール時におそらくSQL Server Local Dbも一緒にインストールされているのですが、古いバージョンのLocal Dbがこのメッセージボックスの原因でした。

実際にSQL Server 2016 Express LocalDB に更新すると、今までのメッセージボックスが嘘のように、現象は回避されました。

インストール手順

下記のサイト様が大変判りやすく記載されています。

www.hiskip.com


自分の備忘録を兼ねて。。。

sqlservr.exe has stopped working

AutoMapper6でのプロファイル設定とユニットテスト

AutoMapperという自動マッピングライブラリーが便利なのです。ただ、少しばかり使い方を誤っていたようで、下記のサイトを参考に再勉強させて頂きました。

iyemon018.hatenablog.com

この記事のお陰で、今までの自分があまりにも恩恵を受けない無駄な記載をいっぱいしていたという事に気づきました。例えば、逆マッピングもわざわざ定義を書いていたとか、同じ名前のオブジェクトもわざわざ定義書いていたりだとか・・・。

いざ自分でも定義をプロファイル単位にしようと試していたのですが、どうも仕様が違っている様子・・・。
最新のAutoMapperは6.0.2になっており、プロファイルのオーバーライド辺りが変更になっていたようです。ですので、自分の備忘録も兼ねて手順を記載します。

マッピングの定義

以前のAutoMapperでは、Configure()メソッドをオーバーライドして定義を書いていましたが、バージョン6以降は互換がなくなったようで、コンストラクターに定義を記載する事になります。

public class HogeProfile : Profile
{
    // コンストラクターでマッピング定義を記載します
    public HogeProfile()
    {
        CreateMap<SourceClass, DestinationClass>()
            .ForMember(d => d.XXXXXXXXXX, o => o.MapFrom(s => s.YYYYYYYYYY))
            .ReverseMap()
            .ForMember(s => s.XXXXXXXXXX, o => o.MapFrom(d => d.YYYYYYYYYY))
            ;
    }
}


上記のように、必要な定義ごとにクラスを分ける方が管理しやすくて良いと思います。以前一つのクラスの中に定義をまとめて書いていたのですが、数が多くなると判りにくくて仕方が無いという経験からの意見です。

作成したプロファイル設定は、ASP.NET MVCであればGlobal.asaxなどに呼び出しの記述を行います。

Mapper.Initialize(config =>
    {
        config.AddProfile<HogeProfile >();
        config.AddProfile<HogeHogeProfile>();
    });

// マッピング設定の検証
Mapper.AssertConfigurationIsValid();

マッピングユニットテスト

基本的にそのまま値の受け渡しが行われるので、ユニットテストは不要という考えもできます。
但し、型が違うことによりAutoMapperで変換をかけたり、固定値を入れたりなど絶対不要か、といえばそうでも無いと思います。ユニットテストを書く癖をつけるためにも書いてみました。

簡単ですが、MSTestで書いたテストクラスはこんな感じです。

[TestClass()]
public class HogeProfileTests
{
    [ClassInitialize]
    public static void ClassInit(TestContext context)
    {
        Mapper.Initialize(config =>
        {
            // Global.asaxの呼び出し代わりにClassInitializeでプロファイルを呼び出しておく
            config.AddProfile<HogeProfile>(); 
        }); 
    }

    [TestMethod()]
    public void マッピング設定の検証()
    {
        // マッピングエラーならException
        Mapper.AssertConfigurationIsValid();
    }

    [TestMethod()]
    public void コピー元からコピー先へのマッピング検証()
    {
        var srcModel = new SourceClass()
        {
            // コピー元の値を適当に設定
        };

        var destModel = Mapper.Map<DestinationClass>(srcModel);

        // コピー元とコピー先で値が同じか
        Assert.AreEqual(destModel.xxxxxx, srcModel.xxxxxx); 

    //以下・・・省略
    
    }


    [TestMethod()]
    public void コピー先からコピー元への逆マッピング検証()
    {
        var destModel = new DestinationClass()
        {
            // コピー先の値を適当に設定
        };

        var srcModel = Mapper.Map<SourceClass>(destModel);

        // コピー元とコピー先で値が同じか
        Assert.AreEqual(srcModel.CategoryId, destModel.CategoryId);

        //以下・・・省略

    }
}


名前や型が同じプロパティー部分は比較検証しなくてもいいかな、というのが率直な感想です。
AutoMapperで何かしらのマッピング定義を施した箇所を中心にテストを作成するのが合理的だと思います。

20年以上使っていてはじめて知ったWindowsの機能

2017年4月11日をもって、Windows Vistaの延長サポート期間が終了しました。
私自身もよくよく考えると、Windows利用歴が20年以上なんですよね・・・。文字にすると恐ろしいな・・・、20年かぁ・・・。

さて、それだけ長年Windowsに触れてきた訳ですが、まだまだ知らないこともたくさんあります。

つい最近も、偶然に知ったWindowsの機能に驚愕していたところです。本当に奥が深いよ、Windows

メッセージボックスに隠された機能

f:id:shin21sk:20170412190524p:plain

よくあるメッセージボックスです。これは、何かを書きかけている最中に閉じるボタンを押すと表示される「よく見かける」メッセージです。

この一見、何の変哲も無いメッセージボックス。

タイトル欄辺りをクリックして、メッセージボックスが選択されている状態にします。

そのまま、Ctrl + C キーを押してクリップボードにコピーします。

そして、適当なメモ帳などを新規で開いて、貼り付けを行うと・・・

[Window Title]
メモ帳

[Main Instruction]
無題 への変更内容を保存しますか?

[保存する(S)] [保存しない(N)] [キャンセル]


テキストとして、コピペができたんです!!


知っていました?かなり衝撃的だったんですけど・・・。

メッセージボックスでエラー内容とかいちいち見ながら打ち込んでたあの日は一体なんだったんだ!?と。


余談ですが、Excelのメッセージボックスではできませんでした・・・。パターンがあるのかな。

【Azure】Microsoft Cognitive ServicesのFace APIを検証したキロク

2017.03.28 大幅加筆しました。

f:id:shin21sk:20170327183607p:plain

Microsoft AzureにはCognitive Services(コグニティブサービス)といって、視覚や聴覚などAI技術を利用して認識を行う超絶便利なサービスがあります。

今回はその中で顔認識「Face API」を利用し、公式ドキュメントの曖昧な部分を検証しました。

Face APIに関しての詳細は、マイクロソフトの公式ドキュメント(英語)をご確認ください。

Microsoft Cognitive Services - Documentation

この通りに実装すれば、いとも簡単に顔認証ができます。本当に、Azureはヤバいです。

FaceAPI Freeプラン

FaceAPIにはFreeプランが存在します。ちょっとした評価や簡単な実装であれば問題ないのですが、少し本格的なサービスを実装しようとするとFreeプランのままでは制約が多いと感じました。
特に最初に判らないのは「トランザクション」の概念かと思います。

レベル 機能 料金
Face API - Free 1 分あたり 20 トランザクションまで 30,000 無料トランザクション / 月
Face API - Standard 最大 10 トランザクション/秒
0 - 1,000,000 トランザクション \153/1,000 トランザクション
1,000,001 - 5,000,000 トランザクション \112.20/1,000 トランザクション
5,000,001 - 20,000,000 トランザクション \66.30/1,000 トランザクション
フェイス ストレージ フェイス ストレージ - 各画像のサイズ上限は 4 MB 画像 1,000 枚あたり月額 - \51

出典
価格 - Face API | Microsoft Azure

ここで挙げられている「トランザクション」とは、何かしらの命令をFaceAPIに送る度にカウントされます。
例えば公式サンプルの記載では、顔認証完了までに最低3トランザクションが必要となっています。

using (Stream s = File.OpenRead(testImageFile))
{
    // 画像ファイルの認証 ->これで1トランザクション
    var faces = await faceServiceClient.DetectAsync(s);
    var faceIds = faces.Select(face => face.FaceId).ToArray();

  // 顔の認証 -> これで2トランザクション
    var results = await faceServiceClient.IdentifyAsync(personGroupId, faceIds);
    foreach (var identifyResult in results)
    {
        if (identifyResult.Candidates.Length == 0)
        {
            Console.WriteLine("No one identified");
        }
        else
        {
            var candidateId = identifyResult.Candidates[0].PersonId;

      // Person情報の呼び出し -> Loop毎にトランザクション数加算
            var person = await faceServiceClient.GetPersonAsync(personGroupId, candidateId);
        }
    }
}

これ以外にも、削除処理でも1件に付き1トランザクションとなりますので、不要なデータを削除したい場合でも注意が必要です。 

Freeプランでは、「1分間に20トランザクションまで」という制約がありますので、ちょっとしたサービスを構築するだけでもスグに上限値には達しそうです。

FaceAPIの階層構成

FaceAPIで写真を登録する場合、実際は3階層に分かれているのですが、サンプル通りに実装すると2階層までしか意識しなくても良い書き方となっています。お手軽に作成する分には何も問題はないのですが、原理を知りたいとか構成を知りたいとか行った場合には気になる所です。後述しますが、FaceAPIは「登録上限数」があるので、階層でどれくらい登録できるのか?は押さえておいて損はないと思います。
具体的には、「PersonGroup」「Person」「PersonFace」の3階層となっています。

f:id:shin21sk:20170328115410p:plain

1.PersonGroup階層

人物のグループを登録する為の階層です。認識の際は、このグループ内で合致する顔があるかどうかで検索が行われます。

最大1,000個のPersonGroupを作成する事が可能です。

PersonGroupには一意のpersonGroupIdが必要で、任意の文字列で登録することが出来ます。但し、使用出来る文字には制約があり数字、英字(小文字)、'-','_'が使用出来ます。特にこだわりがないのであれば、GUIDを作成しその値で作成すると良いと思います。

詳しくは、下記APIリファレンスを参照ください。
Microsoft Cognitive Services

2.Person階層

人単位のグループです。例えば、山田太郎さんの顔写真(複数)を管理する階層です。
新規登録時、PersonIdがGUIDで作成されます。同じ人物に画像を追加する場合はこのGUIDを保持しておく必要があります。

Person情報は、PersonGroup階層下に1,000ユーザー分作成可能です。1,000件を超えるとエラーとなる為、別グループへ登録する等の考慮が必要です。

3.PersonFace階層

人物の顔情報データです。(1画像毎に作成される)

一人の人物に対して、複数枚の画像を登録出来ます。理由としては、同じ人物に複数の顔写真を登録する事により、顔認証の精度が向上する事が可能な為です。
可能であれば、正面を向いた顔・側面・眼鏡の有無などの写真を登録すれば、より精度を高めることが出来ます。

但し、登録に際しては制約条件が数点あります。

  • 登録可能な数は1ユーザー毎に248件
  • 画像の種類は、JPEGPNG、GIF(最初のフレーム)、およびBMPがサポート
  • イメージファイルのサイズは、1KB以上4MB以下
  • 検出可能な顔のサイズは、36×36〜4096×4096ピクセル


特に写真で撮った画像をそのまま認証させたい場合など、画像サイズに注意が必要です。

削除時は下位層を考慮する

FaceServiceClientには各Deleteメソッドが準備されています。

例えば、PersonGroupを削除する場合は「DeletePersonGroupAsync」といった具合です。

但し、上位のPersonGroupを削除したとしても、下位層の「Person」「PersistedFace」は残ったままとなります。

よって、上位を削除する際には、下位層のデータを削除してから消すといった一手間が要りそうです。


削除も1件=1トランザクションで課金の対象となる為、敢えて残すという選択肢もあるとは思います。

値の無いデータは全てNotFoundでExceptionがスローされる

GetPersonGroupAsync等で指定した値に該当するデータが無い場合、nullでは無くExceptionがスローされます。

この辺りは善し悪しなのですが、個人的にはnullで返ってきてもらった方が慣れているので、一手間を加えて使用しています。

まとめ

もう何点か検証中項目がある為、随時更新していきます。

一つ言えるのは、Azure楽しい♪という事です。