Reproduce transactions as new transaction id when repurchasing an auto-renewable subscription

I was building an app which has auto-renewable subscriptions.
When I try to resubscribe the subscription I subscribed before, then iOS shows the dialog, “You’re currently subscribed to this.”, And enqueue all of the transactions I purchased in the past into a default transaction queue. The statuses of the transactions are SKPaymentTransactionStatePurchased. As a result of this behavior, I have to process vast of transactions. This happening was in the Sandbox environment.
 
I expected to enqueue only one transaction as SKPaymentTransactionStatePurchased.
 
The following list is my scenario that reproduces all transactions:
1. Subscribe a product
2. Subscribe the product purchased again
3. The dialog, “You’re currently subscribed to this.”, is popped up
4. Tap ok button
5. Press Home button then close my app
6. Tap and launch my app again

I tried to solve this problem throughout the day. Finally, I created a new sandbox user and just used it for purchasing subscription. Then this behavior has no longer occurred. I don’t know why such things happened.

The test cases for In-App Purchase on iOS

I came up to implement In-App Purchase on iOS with Unity.
I looked for the test cases for In-App Purchase, however, couldn’t find their cases. Therefore, I memorize the test cases I checked myself.

At first, please read In-App Purchase Best Practice. We should implement your In-App Purchase according to this document.

For instance, I implement below list:

  • Add a transaction queue observer at application launch
  • Query the App Store for product information before presenting your app’s store UI

Figure 1 shows the process, second of the list, from showing available products to purchasing them.

Figure 1.

  1. Retrieve product identifiers from a data store
  2. Request products from Apple Store
  3. Show available products on an app

Below, the test cases I checked. the left side of the table are test cases and the right side is expected results.

Purchasing and Subscribing test cases

Test case Expected result
Show a product list scene The product list scene shows buyable products
Buy products All products can buy
Close an app in the middle of a purchasing The item corresponds to the purchased product is given the user next launch
Disconnect internet connection in the middle of a purchasing The items correspond to the purchased are given the user next startup
Purchase a subscription product in advance. Switch an Apple account to another one on the same Device The purchased subscription product must get unbuyable
Purchase a subscription product by the same Apple account on deferent devices (purchase a product on Device-A, then purchase it on Device-B) Must purchase the product on Device-A and must not purchase it on Device-B
Purchase a subscription product with the same Apple account among two devices Must purchase the product on both devices
Purchase a subscription product in advance and close the app completely. Wait until the subscription is updated by Apple Store and launch the app Must give the user the items corresponding to the subscribed product

Restoring

Implement a restoring product for subscription products.

Test case Expected result
Restore the subscription after the subscription product is renewed The app gives the items that corresponding to the subscription to the user | Buy a subscription product with the same Apple account. Restore Device-A, next Device-B
Buy a subscription product in advance. Use both of devices, Device-A and Device-B. Then restore Device-A next Device-B An app gives the items corresponding to the subscription to Device-A, although it doesn’t on Device-B
Launch an app after closing the app in the middle of Restoring Purchased Products Do not execute unprocessed transactions
Sign out from App Store, then launch an app The user is prompted to sign in App Store if unprocessed transactions are existed
Tap Buy a product button multiple times Show only one In-App Purchase dialog
Use unregistered product identifiers in iTunes connect that it’s in part of an app’s buyable item list Do not show the unregistered products in app’s item list

CoreMLを使ったGenerative Adversarial NetworkのiOS Appを作る

English page

iOS上でCoreMLを使って手書き文字を生成するアプリを作成しました

手書き文字の生成からアプリの公開までをまとめておきたいと思います。

使ったソフトウェア

  • Xcode 9.2 (9C40b)
  • Docker 17.09.1-ce
  • TensorFlow docker imagesha256:1bb38d61d261e5c9230a1e60b5d200088eb03014fdb35a91859fa55ea0d2c4d5, https://hub.docker.com/r/tensorflow/tensorflow
  • TensorFlow 1.2.1
  • Keras 2.0.6
  • coremltools 0.7

手書き文字画像の生成からiOS appのリリースまで

図1は手書き文字画像の生成からiOS appのリリースまでの手順を表しています。

the flow of distributing an app
図1. iOS appリリースまでの手順

DockerのインストールしてTensorFlowとKerasが動作するJupyter Notebookを用意する

Kerasモデルを作成するためにDockerをインストールします。

以下のコマンドを実行します。

$ docker run -d --name notebook tensorflow/tensorflow:latest

KerasとCoreML converterをインストールする

$ docker exec -it notebook /bin/bash
$ pip install -U keras
$ pip install -U coremltools

Jupyter Notebookにアクセスできることを確認する

Jupyter NotebookにアクセスするためのURLを取得します。

$ docker logs notebook
# 以下のようなトークンがついたURLが表示されると思います
#
#    Copy/paste this URL into your browser when you connect for the first time,
#    to login with a token:
#        http://localhost:8888/?token=ecf1a2471670eb6863195ab530d6ac1d5cc27511faca0afe

上記のURLをブラウザで開くとアクセスできると思います。
これでKerasのコードを書けるようになります。

Kerasモデルを作成する

Jupyter Notebookに新しいノートを追加してGANモデルのコードを書きます。GANのコードはこちらを参考にしました。このコードをコピーしてcellに貼り付けます。モデルをディレクトリにセーブするために以下のようにコードを追加します。

if __name__ == '__main__':
    gan = GAN()
    gan.train(epochs=30000, batch_size=32, save_interval=200)
    gan.discriminator.save('./discriminator.h5') // discriminatorモデルを保存するディレクトリを指定します
    gan.generator.save('./generator.h5') // generatorモデルを保存するディレクトリを指定します

あとはcellを実行します。MacBook Pro 2016で実行した感じだと25分ほどかかりました。

このcellを実行すると学習済みモデルが指定のディレクトリに”.h5″のファイルが保存されます。

KerasモデルをCoreMLモデルに変換する

$ coremlconverter --srcModelPath ./keras_model.h5 --dstModelPath ./coreml_model.mlmodel --inputNames gunInput --outputNames ganOutput

このコマンドでKerasのモデルをCoreMLのモデルにコンバートします。コンバートが完了すると”.mlmodel”のCoreMLモデルファイルが生成されます。

手書き文字画像を生成するアプリを作成する

手書き文字画像を表示するにはWeb application、iOS、Androidや単純にJupyter Notebookを使う方法がありますが、iOSを触ってみたかったのでiOSにしました。

アプリを作成する

作成したアプリのソースコードはこちらで公開しています。

図2はアプリのスクリーンショットを示しています。


図2. アプリ (GANs generator)のスクリーンショット

このアプリはCoreMLを使用してKerasのモデルから28 x 28の手書き文字画像を生成します。手書き文字画像はUIKitを使用して表示します。また、手書き文字画像を再生成するボタンがあります。

手書き文字画像を生成する

最初にKerasモデルからコンバートしたCoreMLモデルをXcodeのプロジェクトにインポートします。図3のようになっていると思います。


図3. CoreMLモデルのプロパティ

Xcodeにモデルをインポートすると、モデルのクラスが自動的に生成されます。このクラスは手書き文字画像を生成するpredictionメソッドが実装されています。そして、predictionメソッドの第1引数にKerasモデルのinputが指定されます。第1引数のラベルにはCoreML converterの--inputNamesで指定した値が使用されます。この場合だとクラスのpredictionメソッドの第一引数のラベルをganInput、出力された手書き文字画像をganOutputとして定義しています。この定義は図3のように”.mlmodel”ファイルのModel Evaluation ParametersInputsOutputsで確認することができます。

// Create a gan instance
var model = gan()
// Generate a handwritten image
var output = model.prediction()

// render hand-written image
let HIGHT = 27
let WIDTH = 27

for i in 0...HIGHT {
  for j in 0...WIDTH {
    //create the path
    let plusPath = UIBezierPath()

    //set the path's line width to the height of the stroke
    plusPath.lineWidth = Constants.plusLineWidth

    //move the initial point of the path
    //to the start of the horizontal stroke
    plusPath.move(to: CGPoint(
      x: CGFloat(j * 10),
      y: CGFloat(i * 10) + Constants.plusLineWidth / 2
    ))

    //add a point to the path at the end of the stroke
    plusPath.addLine(to: CGPoint(
      x: CGFloat((j * 10) + 10),
      y: CGFloat(i * 10) + Constants.plusLineWidth / 2
    ))

    //set the stroke color
    let index: [NSNumber] = [0 as NSNumber, i as NSNumber, j as NSNumber]
    UIColor(white: CGFloat(truncating: out.gan_out[index]), alpha: CGFloat(1)).setStroke()

    //draw the stroke
    plusPath.stroke()
  }
}

このプロジェクトをXcodeで開いてシミュレーターで確認することができます。

Apple Developer Programに登録する

アプリの動作を確認したら、App Storeにこのアプリを公開します。もし、Apple Developer Programに登録してなけらば、こちらで登録を済ませます。アプリをApp Storeに公開するにはApple Developer Programの登録が必要です。

App Storeに登録する

詳しくは公式ドキュメントを参照してください。

  1. Archiving
    Product > Archive

  2. Validating
    archivingが完了するとArchive organizerValidateができるようになるのでApp Storeにアップロードする前に実行して、アプリに問題がないかチェックしておきます。

  3. App Storeにアプリをアップロードする
    Upload to App Storeをクリックします

アプリを公開する

詳しくは公式ドキュメントを参照してください。

  1. アプリをiTunes connectに登録する
  2. アプリのアイコンとプレビュー、スクリーンショットを追加する
  3. App Reviewに出す
  4. アプリをリリースする

Create a Generative Adversarial Network iOS App with CoreML

I created the app that generates a handwritten image with CoreML on iOS.

I’m going to explain the process to release this app from beginning to end.

The software versions

  • Xcode 9.2 (9C40b)
  • Docker 17.09.1-ce
  • TensorFlow docker imagesha256:1bb38d61d261e5c9230a1e60b5d200088eb03014fdb35a91859fa55ea0d2c4d5, https://hub.docker.com/r/tensorflow/tensorflow
  • TensorFlow 1.2.1
  • Keras 2.0.6
  • coremltools 0.7

The process to make handwritten image generator app on iOS and submit it to the App Store

As shown in figure 1, it illustrates the path of distributing this app through App Store.

  1. Create a GAN (Generative Adversarial Network) model on Keras
  2. Convert a Keras GAN model to CoreML model
  3. Create an app to show handwritten images generated by the CoreML model
  4. Distribute the app

the flow of distributing an app
Figure 1. The path of distributing this app through App Store

Install Docker and run Jupyter Notebook that includes TensorFlow and Keras

At first, To create a Keras model, we have to install Docker.

Install Docker

On Mac, get it in this page at the Stable channel and just install it.

Run Jupiter Notebook with TensorFlow

Fortunately, there is the Docker image that includes TensorFlow.

Just type this command:

$ docker run -d --name notebook tensorflow/tensorflow:latest

Install Keras and CoreML converter

$ docker exec -it notebook /bin/bash
$ pip install -U keras
$ pip install -U coremltools

Check that you can access the Jupyter Notebook

Before access it, you need to get an access token.

$ docker logs notebook
# You will find an access token like this:
#
#    Copy/paste this URL into your browser when you connect for the first time,
#    to login with a token:
#        http://localhost:8888/?token=ecf1a2471670eb6863195ab530d6ac1d5cc27511faca0afe

Copy the URL with an access token and access on your browser.

Now it’s done! You come can execute Keras code!

Make a Keras model

Add a new note to Jupyter Notebook and write a GAN model. I referred to this source code. Copy this code and paste to your Jupyter Notebook’s cell and add the code below. It will save a model in a directory.

if __name__ == '__main__':
    gan = GAN()
    gan.train(epochs=30000, batch_size=32, save_interval=200)
    gan.discriminator.save('./discriminator.h5') // Specify the directory saved the discriminator model
    gan.generator.save('./generator.h5') // Specify the directory saved the generator model

Then just run the cell. It took time about for 25 minutes on my MacBook Pro 2016 to training this model.

Keras create a learned model in the directory where executed the cell. It is followed by “.h5”.

Convert a Keras model to CoreML model

$ coremlconverter --srcModelPath ./keras_model.h5 --dstModelPath ./coreml_model.mlmodel --inputNames gunInput --outputNames ganOutput

This command uses TensorFlow in the background and converts to a CoreML model. After the converting, it makes a CoreML model is followed by .mlmodel in the specified directory. This file is a CoreML converted model from Keras model.

Create an app to show handwritten images

There are some ways to create an app that draws a handwritten image. For instance, as a web application or mobile application. This time, I created an app for iOS.

Create the app

Here is the source code for the app:
https://github.com/yanak/gangen/blob/master/Gangen/HandwrittenImage.swift

figure 2 is the app’s screenshot.


Figure 2. The GANs generator app’s screenshot

This app generates a 28 x 28 handwritten image using the CoreML model converted from Keras, and that renders the image by UIKit. There is the regenerate button that regenerates a handwritten image.

Generate a handwritten image

First, import the CoreML model converted from the Keras model to Xcode project directory. It shows like figure 3:


Figure 3. the CoreML model properties

After Xcode imports, a model, the class of a model is automatically generated by Xcode. this class has the prediction method and it specifies Keras model’s input as the first argument. The first argument’s label uses the name of the CoreML converter’s --inputName option value. In this case, The class defines the label of prediction as ganInput and a generated handwritten image’s shape as ganOutput. You can see its definitions in the Model Evaluation Parameters in the CoreML model(see figure 3.)

// Create a gan instance
var model = gan()
// Generate a handwritten image
var output = model.prediction()

// render hand-written image
let HIGHT = 27
let WIDTH = 27

for i in 0...HIGHT {
  for j in 0...WIDTH {
    //create the path
    let plusPath = UIBezierPath()

    //set the path's line width to the height of the stroke
    plusPath.lineWidth = Constants.plusLineWidth

    //move the initial point of the path
    //to the start of the horizontal stroke
    plusPath.move(to: CGPoint(
      x: CGFloat(j * 10),
      y: CGFloat(i * 10) + Constants.plusLineWidth / 2
    ))

    //add a point to the path at the end of the stroke
    plusPath.addLine(to: CGPoint(
      x: CGFloat((j * 10) + 10),
      y: CGFloat(i * 10) + Constants.plusLineWidth / 2
    ))

    //set the stroke color
    let index: [NSNumber] = [0 as NSNumber, i as NSNumber, j as NSNumber]
    UIColor(white: CGFloat(truncating: out.gan_out[index]), alpha: CGFloat(1)).setStroke()

    //draw the stroke
    plusPath.stroke()
  }
}

Open this project, you can check to run it in a simulator like this:

Enroll Apple Developer Program

After check running the app, submit it to the App Store! If you don’t enroll Apple Developer Program yet, you need to do it in here because it needs to submit the app to the App Store.

Submit to the App Store

In detail, see Submitting Your Apps

  1. Archiving
    To archive, on Xcode, Product > Archive

  2. Validating
    Xcode shows the archived app in the Archives organizer. Validate an archived app before uploading to App Store.

  3. Upload to iTunes connect
    Click Upload to App Store

Publish app

In detail, see iTunes Connect Developer Help page.

  1. Add an app in iTunes Connect
  2. Add app icon, app previews and screenshots.
  3. Submit an app to App Review
  4. Release an app
    

Use native code on iOS with Unity Native Code in the Unity

First, write native code you used for iOS. Read this document

I needed to write native code for the In-App Purchase on iOS.

Then, just put your native code in the Assets/Plugins/iOS directory. The hierarchy like this;

For example, using Objective-C, Assets/Plugins/iOS/sample.m and Assets/Plugins/iOS/sample.h.

Build on Unity

In File > Build Settings, switch iOS platform and then click Build and Run. Next, when the build complete, launch Xcode and build the project created by Unity on it automatically.

Build on Xcode

Initial build might occur a signing problem that is Team is None. To solve this problem, just select a team in the pull-down menu of the team. Automatically sign to your app as the team you selected.

Click the build button top left of the Xcode window!

Receive a callback as JSON from an iOS code

Set Game Object on a Unity script before call the native code

var gameObject = GameObject.Find("PurchaseHandler");
            if (gameObject != null) return;

            gameObject = new GameObject("PurchaseHandler");
            if (UnityEngine.Application.isPlaying)
            {
                GameObject.DontDestroyOnLoad(gameObject);
            }

            gameObject.AddComponent<PurchaseHandler>();

Call UnitySendMessage on iOS

sample.m

void GetProductList(const char* json)
{
    ...
    NSString *jsonString = @"{list: [ ... ]}"
    UnitySendMessage([@"PurchaseHandler" UTF8String], [@"onSuccess" UTF8String], [jsonString UTF8String]);
}

Receive a message of UnitySendMessage

public class PurchaseHandler : MonoBehaviour
    {
         private IEnumerator onSuccess(string message)
        {
            yield return doSomething(message);
        }
}

How to find out bottlenecks in HTTP server and MySQL

I participated in ISUCON for the first time.

I acquired some knowledge to find out bottleneck in HTTP server and MySQL. Our team used alp for HTTP response analysis and pt-query-digest for MySQL slow query analysis.

I try to introduce these tools that were useful in our situation or might also other teams(or situations).

How to find the bottleneck of web APIs on web application

We need to analyze access log of API generated with Nginx to decrease API’s response time.

So, I used alp to analyze access log.

Download alp binary proper your architecture, usually amd64 if you use Linux, from here

$ wget https://github.com/tkuchiki/alp/releases/download/v0.3.1/alp_linux_amd64.zip
$ unzip alp_linux_amd64.zip

Change Nginx log formart to LTSV format

$ sudo vim /etc/nginx/nginx.conf
log_format ltsv "time:$time_local"
                "\thost:$remote_addr"
                "\tforwardedfor:$http_x_forwarded_for"
                "\treq:$request"
                "\tstatus:$status"
                "\tmethod:$request_method"
                "\turi:$request_uri"
                "\tsize:$body_bytes_sent"
                "\treferer:$http_referer"
                "\tua:$http_user_agent"
                "\treqtime:$request_time"
                "\tcache:$upstream_http_x_cache"
                "\truntime:$upstream_http_x_runtime"
                "\tapptime:$upstream_response_time"
                "\tvhost:$host";

access.log ltsv;

Analyze Nginx log

$ ./alp -f /var/log/nginx/access.log

The list analyzed is like this;

+-------+-------+-------+---------+-------+-------+-------+-------+--------+------------+------------+-------------+------------+--------+-----------------------------------------------------+
| COUNT |  MIN  |  MAX  |   SUM   |  AVG  |  P1   |  P50  |  P99  | STDDEV | MIN(BODY)  | MAX(BODY)  |  SUM(BODY)  | AVG(BODY)  | METHOD |                         URI                         |
+-------+-------+-------+---------+-------+-------+-------+-------+--------+------------+------------+-------------+------------+--------+-----------------------------------------------------+
|    12 | 0.000 | 0.000 |   0.000 | 0.000 | 0.000 | 0.000 | 0.000 |  0.000 |    318.000 |    318.000 |    3816.000 |    318.000 | GET    | /favicon.ico                                        |
|     1 | 0.000 | 0.000 |   0.000 | 0.000 | 0.000 | 0.000 | 0.000 |  0.000 |   4162.000 |   4162.000 |    4162.000 |   4162.000 | GET    | /js/chat.js                                         |
|     6 | 0.770 | 1.932 |   7.167 | 1.194 | 0.770 | 0.988 | 1.628 |  0.432 | 327999.000 | 327999.000 | 1967994.000 | 327999.000 | GET    | /icons/b1c8c5bc9b026507ae62e6bf62cf55f70f4ac3d8.png |
|     7 | 0.212 | 1.967 |   7.447 | 1.064 | 0.212 | 0.998 | 1.501 |  0.536 | 105173.000 | 105173.000 |  736211.000 | 105173.000 | GET    | /icons/e58ed7194ed4a99d9d6ada9e3383983edcbd1edc.png |
|   301 | 0.099 | 3.752 | 473.578 | 1.573 | 0.549 | 1.507 | 2.921 |  0.407 |      0.000 |   3005.000 |  885578.000 |   2942.120 | GET    | /fetch                                              |
+-------+-------+-------+---------+-------+-------+-------+-------+--------+------------+------------+-------------+------------+--------+-----------------------------------------------------+

This table shows the API response times that are listed in ascending order of time.

We started to improve the response time on worst. This case, we should start at /fetch and /icon.

How to find bottleneck queries on MySQL

To find bottleneck of SQL queries, we used pt-query-digest.

Install percona toolkit

See this document.

$ wget https://repo.percona.com/apt/percona-release_0.1-4.$(lsb_release -sc)_all.deb

$ sudo dpkg -i percona-release_0.1-4.$(lsb_release -sc)_all.deb

$ sudo apt-get update

$ sudo apt-cache search percona

$ sudo apt-get install percona-toolkit
$ sudo vim /etc/mysql/my.cnf
# Enable slow query log
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow_query.log

Analyze slow query log

$ pt-query-digest /var/log/mysql/slow_query.log

The list analyzed is like this;

# 660ms user time, 30ms system time, 39.37M rss, 121.42M vsz
# Current date: Sat Oct 21 14:54:35 2017
# Hostname: app1053
# Files: /var/log/mysql/slow-query.log
# Overall: 1.50k total, 23 unique, 2.73 QPS, 0.01x concurrency ___________
# Time range: 2017-10-21T05:45:19 to 2017-10-21T05:54:29
# Attribute          total     min     max     avg     95%  stddev  median
# ============     ======= ======= ======= ======= ======= ======= =======
# Exec time             3s     1us   608ms     2ms     5ms    20ms   103us
# Lock time           78ms       0     7ms    51us   131us   174us    36us
# Rows sent          1.96k       0     100    1.33    0.99    8.72       0
# Rows examine       2.91M       0   9.77k   1.98k   9.33k   3.74k       0
# Query size        90.88k      20     202   61.88  136.99   25.68   62.76

# Profile
# Rank Query ID           Response time Calls R/Call V/M   Item
# ==== ================== ============= ===== ====== ===== ==============
#    1 0x16A13978F3B7FCFA  1.7328 55.1%     6 0.2888  0.06 SELECT image
#    2 0x4866C96BA295C56E  0.7782 24.7%   301 0.0026  0.00 SELECT message
#    3 0xEBDB62606B914A34  0.4282 13.6%    90 0.0048  0.00 INSERT channel
#    4 0x91283C307570F439  0.0334  1.1%     4 0.0083  0.00 SELECT message
#    5 0x41883793B68D128E  0.0302  1.0%   100 0.0003  0.00 SELECT user
# MISC 0xMISC              0.1427  4.5%  1003 0.0001   0.0 <18 ITEMS>

# Query 1: 6 QPS, 1.73x concurrency, ID 0x16A13978F3B7FCFA at byte 190178
# Scores: V/M = 0.06
# Time range: 2017-10-21T05:46:17 to 2017-10-21T05:46:18
# Attribute    pct   total     min     max     avg     95%  stddev  median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count          0       6
# Exec time     55      2s   203ms   608ms   289ms   580ms   136ms   242ms
# Lock time      1   840us   109us   193us   140us   185us    29us   152us
# Rows sent      5     103      10      23   17.17   22.53    4.06   17.65
# Rows examine   0   5.87k    1001    1001    1001    1001       0    1001
# Query size     0     474      79      79      79      79       0      79
# String:
# Databases    isubata
# Hosts        app1051
# Users        isucon
# Query_time distribution
#   1us
#  10us
# 100us
#   1ms
#  10ms
# 100ms  ################################################################
#    1s
#  10s+
# Tables
#    SHOW TABLE STATUS FROM `isubata` LIKE 'image'\G
#    SHOW CREATE TABLE `isubata`.`image`\G
# EXPLAIN /*!50100 PARTITIONS*/
SELECT * FROM image WHERE name = '78a9228a393eb2621f346fc6a5e099d5bc373f76.png'\G

# Query 2: 1.08 QPS, 0.00x concurrency, ID 0x4866C96BA295C56E at byte 177211
# Scores: V/M = 0.00
# Time range: 2017-10-21T05:46:16 to 2017-10-21T05:50:54
# Attribute    pct   total     min     max     avg     95%  stddev  median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count         20     301
# Exec time     24   778ms     2ms     9ms     3ms     5ms   966us     2ms
# Lock time     15    12ms    30us   208us    39us    66us    15us    33us
# Rows sent     15     301       1       1       1       1       0       1
# Rows examine  98   2.87M   9.77k   9.77k   9.77k   9.77k       0   9.77k
# Query size    19  17.58k      58      60   59.81   59.77    0.85   59.77
# String:
# Databases    isubata
# Hosts        app1052 (298/99%), app1051 (3/0%)
# Users        isucon
# Query_time distribution
#   1us
#  10us
# 100us
#   1ms  ################################################################
#  10ms
# 100ms
#    1s
#  10s+
# Tables
#    SHOW TABLE STATUS FROM `isubata` LIKE 'message'\G
#    SHOW CREATE TABLE `isubata`.`message`\G
# EXPLAIN /*!50100 PARTITIONS*/
SELECT COUNT(*) as cnt FROM message WHERE channel_id = '733'\G

This table shows the queries time are listed in desacending order of time.

In this case, we should start to improve the queryt time that is SELECT * FROM image WHERE name = '78a9228a393eb2621f346fc6a5e099d5bc373f76.png'\G. For example to improve it, use caching on Nginx.

I implemented GANs

I implemented GANs(Generative Adversarial Networks) because I want to learn about GANs with TensorFlow.

I referred to here.

And this is my code implemented GANs. This code almost same what I referred.

My GANs repeatedly learn with MNIST handwritten image 100k times.

These are the learned images 0, 1k, 50k, 100k steps.

0 step


The 0 step image is just noise.

1k steps


The 1k steps image is just noise yet.

5k steps


The 5k steps image look like a handwritten image, but it is a little bit noisy.

50k and 100k steps


50k steps


100k steps

The 50k and 100k steps image pretty look like a handwritten image.

Above implementation is the vanilla GAN. So by using extended GAN like DCGAN would be able to increase the precision of generated handwritten image. And I’ll try to use some other training data.

However, I am going to implement GAN on iOS with CoreML before I use DCGAN and other training images.

I went to iOSDC 2017

I went to iOSDC 2017 on September 17th.
This conference held for iOS developers on September from 16th to 17th at Waseda University.

I heard these sessions:

飛び道具ではないMetal
This session told us how to use Metal to render an image on display. What most interesting is that to draw a picture without Metal is faster than with it.

Apple TV – tvOS入門 –

React Native vs. iOSエンジニ

I had no time to listen to all the sessions this time. And I haven’t had knowledge of developing iOS. So There were some sessions I couldn’t make sense. Now, I’ve been developing an iOS app with Swift!