XCodeのコンソールに出力されるCloudFirestoreのindex生成URLがうまく機能しなかった

はじめに

CloudFirestoreを使用してFirestore.firestore()でqueryを生成しデータアクセスをする際に、whereFieldやorderでデータの絞り込みや順番を変更することができます。この際に、指定条件によってはIndexの生成をコンソール経由で下記のように提案されます。

[Firebase/Firestore][I-FST000001] Listen for query at Data failed: The query requires an index. You can create it here: https://console.firebase.google.com/project/{project-name}/database/firestore/indexes?create_index=xxxxxxxxxxxxxxx

通常はyou can create it here:以下のURLの遷移先のFirebase Console上でindexの生成が提案されるのですが、特定のケースでURL先への遷移がうまく行かないことがあったので、事象の原因と解消方法を記載します。

環境設定

以下の環境を使用しています。

  • Xcode10.1
  • Swift4.2
  • FirebaseFirestore 1.0.2

発生事象および条件と解消方法

発生する事象は遷移後の画面がホワイトアウトし、正常に遷移できないというものです。

本事象は複数Googleアカウントを使用している場合に発生する可能性があります。 Index生成アドレスをブラウザに入力し、実際に遷移した後のURLを見るとわかりますが、下記のように/u/0/ がアドレス内に現れます。

https://console.firebase.google.com/u/0/project/{project-name}/database/firestore/indexes?create_index=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

元々のURLに /u/x の指定がないため本事象が発生しているようです。 複数アカウントを使用していると、この部分が本来 /u/1/u/2 などでなければならない場合があり、その場合に遷移に失敗してしまいます。 /u/0/u/{Number} に書き換えれば問題なく遷移できました。({Number}部分は実際に使用しているアカウントのコンソール上から下記の画像のように確認できます)

f:id:Iganin:20190323131906p:plain

ML Study Jams : Machine Learning 初心者向けトレーニングプログラム の振り返り

はじめに

[ML Study Jams] (https://events.withgoogle.com/ml-study-jams-japan-2019-01/)はGoogleにより提供されるMachine Learningのトレーニングコースです。 2月13日 - 3月3日の期間で開催されるオンライントレーニングコースであり、参加者には無料のQWIKLABSクーポンの配布と、全7種類のコースに対して4種類以上完了した参加者にはノベルティプレゼントの特典があります。 コースは以下です。(Google Developerサイトより引用)

BQML で分類モデルを使用して訪問者の購入を予測する(日本語)
BigQuery ML 予測モデルによるタクシー運賃の予測(日本語)
Cloud TPU: Qwik Start(日本語)
Google Cloud Speech API: Qwik Start(英語)
Cloud Natural Language API: Qwik Start(英語)
Speech to Text Transcription with the Cloud Speech API(英語)
Entity and Sentiment Analysis with the Natural Language API(英語)

終了からしばらくたってしまいましたが、備忘と振り返りも兼ねて学んだこと等を箇条書きに近い形ではありますがまとめます。

API使用について

以下の4種類のコースは主にGoogle提供のモデルを用いた分類APIの使用方法とリクエスト形式およびレスポンス形式に関して学ぶ内容になります。

  • Google Cloud Speech API: Qwik Start(英語)
  • Cloud Natural Language API: Qwik Start(英語)
  • Speech to Text Transcription with the Cloud Speech API(英語)
  • Entity and Sentiment Analysis with the Natural Language API(英語)

BQML で分類モデルを使用して訪問者の購入を予測する(日本語)

BQML(Big Query Machine Learning)を使用したE-commerceデータから訪問者の将来の購入を予測するモデルを作成し、評価、使用するエクササイズです。

モデルを作成・評価する際のデータ使用に関して

  • モデルの作成に全てのデータは使用しない、なぜならモデルの精度の評価に使用するデータが必要なため
  • モデルの改善を行う場合、旧来モデルとの比較を行いたい場合はモデルの学習およびモデルの評価に使用するデータセットは同じにする(通常の評価試験と同じ考えですね)

BQMLのコマンドについて

  • ML.EVALUATEでモデルの評価を行う
  • roc_aucを使用することで、false negativeを避ける、 true positiveを採用するという観点からのモデルの精度を評価できる
  • CREATE OR REPLACE MODEL モデル名 OPTIONS (model_type='モデルタイプ', labels = ['予測したいパラメータ']) でMLモデルの作成を行う
  • model_typeは数値予測(来年度の売上など)の場合はlinear_reg, 2値分類(購入するか否かなど)の場合は logistic_regを使用する

  • 学習モデルを作成する際には、どのデータを学習に使うか、どのデータが推測したい値かをきめる

  • 学習用パラメータが十分かどうか(種類、量)を評価によって考察することが重要
  • Modelの学習は warm_start = trueをつけることで早くなる

Modelの評価の方法について

SQLの関数について

CONCAT
// 値のつなぎこみを行う、例えば
CONCAT(Id, name) -> 1005 sato

UNNEST
key | array
No | [1, 2, 3, 4]のようなデータ構造に対して使用した場合 []のネストをなくす
UNNEST(array) をした場合

No | 1
No | 2
No | 3
No | 4

というような形になる

BigQuery ML 予測モデルによるタクシー運賃の予測(日本語)

数値予測を行うためにlinear regを使用して学習モデルの作成を行うエクササイズです。

SQL関数について

TIMESTAMP_TRUNC(https://cloud.google.com/bigquery/docs/reference/standard-sql/timestamp_functions)
TimeStampの truncate -> TIMESTAMP_TRUNC(TIMESTAMP, Truncateの単位(MONTHなど))

EXTRACT(単位、 timestamp) -> 単位部分をtimestampから抜き取る EXTRACT(HOUR, timestamp) -> 3時など

TIMESTAMP_DIFF(timestamp1, timestamp2) 2つのtimestampの時間差を秒で返す
ORDINAL -> 番号付が1から
OFFSET -> 番号付が0から

線形回帰モデルの評価について

  • 予測モデルでは、標準偏差(Root Mean Squared Error)が評価の対象になる(当然0に近いほうが良い)
  • 受け入れ可能かどうかは、学習を開始する前に決めた Criteria(おそらくビジネス要件で決まる?)により決定する

  • ML.TRAINING_INFO => EXPLORED IN DATA STUDIOで学習状況の時系列等を確認できる

  • ここからモデルが過学習(over fitting)になってしまっていないかどうかがわかる

Cloud TPU: Qwik Start(日本語)

TPU -> Tensor Processing Unit 機械学習に最適化されたCPUに関して学ぶセッションです。

コマンドなど

ctpu print-config
ctpu print-config コンフィギュレーションの確認
ctpu up --zone ${ZONE}
ZONEでの Compute Engine and Cloud TPU servicesを使用可能にする(ほか諸々のタスクも行う)
  • TPUはTensorFlowを使用する
  • ctpu(tpuを使用するコマンド)はGCPのshellに備え付けられている

2019年2月振り返り

2月の振り返り

  目標の進捗状況の定期確認です。 2月はにわかに業務負荷が高まり、プライベートの時間確保が難しくなりました。 隙間の時間をいかに活用するか、多忙な時期でも活動を続けられる精神的、肉体的なタフネスをいかにつけるかが課題だと感じています。  

体の基礎づくり

ベンチプレスが80kgで安定してきました。 スクワットも先月の100kgと比較して前進しています。デッドリフトの伸びが弱いです。  

アウトプット

ブログ投稿

  • 目標: 100記事 / 年
  • 実績: 本記事も合わせて6記事

目標達成のためには 8記事/月は必要なためショートしています。  

登壇

  • 目標: 6件 / 年 
  • 実績: 登壇なし 全1件

特に進捗ありません。

サービス開発(アプリに限定しない )

  • 目標: 3件
  • 実績: 0件

アプリを作ろうという計画を立てました。

スキルアップ

AtCoder

  • 目標: 水色
  • 実績: 茶色

1回参加できレートが100上がりました。 なかなか時間が取れずにいますが、この調子で着実に進歩していきたいところです。

Git

  • 目標: 1000 commit / 年
  • 実績: 14 commit / 月

全然ダメです。 3月はサービス開発に時間がさける想定のため、そこで頑張りたいところです。

その他

  • iOSアプリ設計パターン入門 残りReduxの応用の章を残すのみとなりました。
  • WWDCが近づいてきたので、開始日時までにiOS12Programmingを読破したいです。
  • Redux & RxSwiftの組み合わせをプライベートのサービス開発で実践したいです。

3月も引き続き頑張ります。

「オペレーティングシステムの仕組み」の感想

読んだ本 

オペレーティングシステムの仕組み (情報科学こんせぷつ)

オペレーティングシステムの仕組み (情報科学こんせぷつ)

題名通りオペレーティングシステムに関する書籍です。 大学の講義の副読本や情報工学が専門外の方がカフェなどで肩に力を入れずゆったり読むのに調度良い難易度と分量かと思いました。 特に数式等も出てこず、一部C言語でのコード記載もありますが、ポインタの基礎的な理解があれば十分そうです。 高校3年次もしくは大学初年度の方、もしくは情報工学を専攻せずにIT関連の職についた方がOSの基礎的な理解を欲したときに読むのに適しているように感じます。

 

感想など

私自身、大学では情報工学とは関係ない専攻だったため、本書の記載から学ぶことは大いにありました。 概要という形では目次をみていただくのが良いかと思いますが、プロセス・スレッド、排他制御と同期、メモリ管理、アクセス制御など普段アプリ開発を行なっている際にも よく見かける内容に関しての概念や実装がとても丁寧に記載されています。 上記に関しては基本情報処理技術者試験や応用情報処理技術者試験を受けられた方は既知の内容かもしれませんが、 より詳しく、ハードウェアとの関わりやC言語でのコード記載も含めながら記されていますので、上記の経験がある方も一読するとより理解が深まるかと思います。

本書を読んでいてPersonal Computerが説明のベースにあるのかなと感じましたので、 今後は iOS, Androidなどのモバイル端末OSの仕組み、 HDD以外の2次記憶装置との間のページングアルゴリズムあたりを調べられたらなと感じました。 特に自分が主に業務で扱っているモバイル端末におけるOSの仕組みとPCのOSの仕組みとの間にどのような差異があるのか、 どういった要請によりそのような差異が生じざるを得なかったのかに興味があります。(ほとんど差はなさそうな気もしますが)

OSの役割について、抽象化というキーワード出てきます。

  • ハードウェアに依存した詳細の隠蔽
  • ハードウェアの持つ物理的な制約の緩和
  • ハードウェアの共有

上記を実現し、ハードウェアを気にせずに処理を行うことができるようにする役割をOSは果たしています。

「抽象化」という概念はOS以外にもみられます。 サーバやDBなどのフルマネージドサービスがクラウドにより提供されることにより、パッチ処理やDBの拡張、物理的なラック構成などオンプレミスで発生する 通常の運用・保守業務のことは気にせずサービスに集中できるようになっています。また、Swiftなどの言語ではメモリアクセスやメモリ管理をあまり気にせずプログラムを作成できるようになっています。 (Objective-Cの初期のMRC方式ではこの辺りなかなか大変だったはずです、C言語のメモリ管理もなかなか骨が折れますね。) 「抽象化」はより豊かなサービスや世界を作るキーワードのひとつだなと感じています。それが進むことによって、下位レイヤーを気にせず、 より簡単により豊かなものを作ることができるようになります。今後どのレイヤーが抽象化されていくのか、それによってどのような変化が生まれるか、はITに限らず興味深い題材の一つですね。

SwiftにおけるArraySliceについて

はじめに

SwiftではArrayに対して、その配列の部分配列を取得しようとするとArrayではなくArraySliceが返却されます。 なぜArrayでないのか、ArraySliceを使用する理由はなんなのか気になったので調べてみました。

環境設定

以下の環境を使用しています。

  • Xcode10.0
  • Swift4.2

ArraySliceについて

Appleのドキュメントを確認してみます。

The ArraySlice type makes it fast and efficient for you to perform operations on sections of a larger array. Instead of copying over the elements of a slice to new storage, an ArraySlice instance presents a view onto the storage of a larger array. And because ArraySlice presents the same interface as Array, you can generally perform the same operations on a slice as you could on the original array.

For more information about using arrays, see Array and ContiguousArray, with which ArraySlice shares most properties and methods.

ArraySlice - Swift Standard Library | Apple Developer Documentation

重要な部分をかいつまんでみてみると、以下のようになります。

新しい領域を確保してArrayの要素をコピーするのではなく、ArraySliceはArrayへのviewを表します。
またArraySliceはArrayと同様なインターフェースを備えているためArrayに実施するのと同様な操作をArraySliceに行うことができます。

つまりArraySliceは新規でメモリ領域の確保は行わず、元の配列を参照しながら 始点のindexとCountを保持していると考えられます。

コードでの確認

上記の確認のため実際にポインタのアドレスを見てみました。 わかりやすさのために prefixを使用しています。 ポインタを確認すると、同一であることがわかります。 したがって、 a とその部分配列であるbは同一のアドレスをさしていることがわかります。

let a = [1, 2, 3, 4, 5]
var b = a.prefix(3)

print(UnsafePointer(a))
b.withUnsafeBufferPointer { (pointer) -> Void in
    print(pointer)
}
0x00006000011fcac0
UnsafeBufferPointer(start: 0x00006000011fcac0, count: 3)

値の代入を行った場合にどうなるかも確認してみました。 参照のみ保持している場合は元のArrayの値まで変わってしまうのでしょうか。 結果としては下記のように値を変更したタイミングで新規にメモリ領域が確保されました。

let a = [1, 2, 3, 4, 5]
var b = a.prefix(3)

print(UnsafePointer(a))
b.withUnsafeBufferPointer { (pointer) -> Void in
    print(pointer)
}
b[2] = 9
b.withUnsafeBufferPointer { (pointer) -> Void in
    print(pointer)
}
0x00006000010e0de0
UnsafeBufferPointer(start: 0x00006000010e0de0, count: 3)
UnsafeBufferPointer(start: 0x00006000026f2620, count: 3)

この動きはStructにおけるコピーオンライトと同様の動きになります。 つまり、変数への格納時ではなく値が変わった際にメモリ領域の確保が行われます。

let a = [1, 2, 3, 4, 5]
var c = a
print(UnsafePointer(a))
print(UnsafePointer(c))

c[0] = 1
print(UnsafePointer(c))
0x0000600000622500
0x0000600000622500
0x00006000006360a0

なぜArraySliceを使うのかの考え

一番の理由は単純にパフォーマンスではないかと思います。 あたらしいメモリ領域を確保し、そこにArrayの要素をコピーするよりは元のデータの参照を保持し、 そのどの部分配列を見れば良いかというデータのみを持つ方がパフォーマンスが良いのではないだろうかと直感的に考えます。

終わりに

ArraySliceで返ってくると Array型に変換しなければArrayで定義してあるメソッドの引数に渡すこと等ができず、 正直なところArrayで返してくれればいいのになと思っていたのですが、調べてみてArraySliceも必要だなと考えが変わりました。

DroidKaigi2019に参加しました

はじめに

2/7(木)- 2/8(金)に開催されたDroidKaigi 2019に参加してきました。 その感想になります。

本文

DroidKaigiはAndroid関連の技術に関する技術カンファレンスです。 ベルサール新宿グラントで開催されました。 ざっとセッションを見渡すと、以下のような内容が多いのかなと感じました。

個人的に良いなと思ったセッション

自分は普段はiOSをメインで開発しているため、 Androidの具体的な技術の話より 思想やデザインツールとの関わり、クロスプラットフォームのFlutterに関連したセッションにより興味をそそられました。 ※もちろんAndroidの技術よりの話も大変面白かったです。マルチモジュール周辺の話はiOSにも応用できそうな点がありました。

とくに良いと思ったセッションは以下の二つです。

マテリアルデザインの起源とベースとなる哲学

マテリアルデザインの起源とベースとなる哲学 - Speaker Deck

Androidアプリのデザインガイドラインであるマテリアルデザインに関するセッションでした。 デザインの歴史を生産性/合理性と多様性/個性の軸で見直した上で、現代のデザイン環境を定義を行い、 それを元にマテリアルデザインのMatias Duarteのデザイン哲学に迫る大変意欲的な内容でした。 マテリアルデザインのドキュメントにはまだあまり目を通せていませんが、本セッションの内容を 踏まえた上ですとより理解が進むのではないかと感じています。

デザイナーとエンジニアの距離をより近づける Lottie 利用術

デザイナーとエンジニアの距離をより近づけるLottie利用術 - Speaker Deck

アニメーションを描画するライブラリであるLottieとその周辺技術および利用方法に関するセッションでした。 Lottieに関わる技術(AE, AI, bodymovin)と、 Lottieで取り込む際のJSONファイル作成までのワークフロー、 およびLottieでJSONからアニメーションがどのように生成されるかの概要を理解することができました。 Lottieは社内でも導入しているプロジェクトがいくつかあり、また個人的にも関心を持っていたので とても興味深い内容でした。iOS側のJSON -> アニメーションの変換も時間がある際にみてみたいと思います。

最後に

セッションの内容の充実度ももちろんのことですが、技術を向上していこう、よりよいものを作っていこう、 良い方法を学ぼう、そういったポジティブな気持ちで満ちた空間そのものがとても素敵だなと感じました。 企業ブースも大変充実しており、展示をみているだけでもとても楽しかったです。 バリスタさんがいれてくださったコーヒーも大変美味しかったです。

AndroidやKotlinなどに関してもっと学びたい、今回学んだことを早速プロダクトに活かしていきたいと感じました。 来年も参加したい(できればいつかはSpeakerで。。。)と思います。 主催の方々、スタッフの方々、スピーカーのみなさんありがとうございました。

Swiftにおける Voidと空Tuple - ()

はじめに

本日、UZUMAKIさん主催の「iOSアプリ設計パターン入門」の勉強会に参加しました。

内容はMVVMアーキテクチャに関してでしたが、議論の中でVoidや()の扱いに関して面白いものがでてきたので備忘もかねて記載します。

環境設定

以下の環境を使用しています。

  • Xcode10.0
  • Swift4.2

Void と 空Tupleについて

Voidは以下の形式で表される「型」です。

public typealias Void = ()

そのため、メソッドの引数等にとることはできません。 その際にはInstanceを入れる必要があります。 多くの場合、このような場合の引数として () を使用すると思います。

let sampleRelay = PublishRelay<Void>()

// このように Voidで型指定されている部分に ( ) を入れる。
sampleRelay.accept(()) 

ただ、 Voidが () の typealiasであることから、以下のように () は型としても機能します。

let a: () = ()

また、 Voidは型であるため以下のようにインスタンス化も可能です。

let a = Void()

したがって、 ()を代入する箇所では Void()の代入でも代替可能です。 ただ、 Void()はできますが、 ()()はできません。 Cannot call value of non-function type '()'というエラーが表示され、コンパイルエラーとなります。

まとめ

以上、取り止めがないですがまとめると下記のようになることがわかりました。

// Void は 型、 ()は型としてもインスタンスとしても機能する

let a: () = ()  // OK
let b = ()  // OK
let c = Void() // OK
let d: Void = () // OK
let e = ()() //  NG
let f: () = ()() //  NG

この辺りの言語仕様は普段あまり意識しませんが、色々触ってみると面白いですね。 個人的にはVoidがインスタンス化可能であることが意外でした。