블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

텐서플로우의 세션,그래프 그리고 함수의 개념


조대협 (http://bcho.tistory.com)


그래프와 세션에 대한 개념이 헷갈려서, 좋은 샘플이 하나 만들어져서 공유합니다.

텐서 플로우의 기본 작동 원리는 세션 시작전에 그래프를 정의해놓고, 세션을 시작하면 그 그래프가 실행되는 원리인데, 그래서 이 개념이 일반적인 프로그래밍 개념과 상의하여 헷갈리는 경우가 많다


즉, 세션을 시작해놓고 함수를 호출하는 케이스들이 대표적인데

http://bcho.tistory.com/1170 코드를 재 사용해서 이해해보도록 하자


이 코드를 보면, tt = time * 10 을 세션 시작전에 정의해놨는데, 이 코드를 함수로 바꾸면 아래와 같은 형태가 된다.


변경전 코드

def main():

   

   print 'start session'

   #coornator 위에 코드가 있어야 한다

   #데이타를 집어 넣기 전에 미리 그래프가 만들어져 있어야 함.

   batch_year,batch_flight,batch_time = read_data_batch(TRAINING_FILE)

   year = tf.placeholder(tf.int32,[None,],name='year')

   flight = tf.placeholder(tf.string,[None,],name='flight')

   time = tf.placeholder(tf.int32,[None,],name='time')

   

   tt = time * 10

   summary = tf.summary.merge_all()

   with tf.Session() as sess:

       summary_writer = tf.summary.FileWriter(LOG_DIR,sess.graph)

       try:


           coord = tf.train.Coordinator()

           threads = tf.train.start_queue_runners(sess=sess, coord=coord)


           for i in range(5):

               y_,f_,t_ = sess.run([batch_year,batch_flight,batch_time])

               print sess.run(tt,feed_dict={time:t_})

               #summary_str = sess.run(summary,feed_dict=feed_dict)

               #summary_writer.add_summary(summary_str,i)

               summary_writer.flush()         


변경후 코드

def create_graph(times):

   tt = times * 10

   return tt


def main():

   

   print 'start session'

   #coornator 위에 코드가 있어야 한다

   #데이타를 집어 넣기 전에 미리 그래프가 만들어져 있어야 함.

   batch_year,batch_flight,batch_time = read_data_batch(TRAINING_FILE)

   year = tf.placeholder(tf.int32,[None,],name='year')

   flight = tf.placeholder(tf.string,[None,],name='flight')

   time = tf.placeholder(tf.int32,[None,],name='time')

   

   r = create_graph(time)

   

   summary = tf.summary.merge_all()

   with tf.Session() as sess:

       summary_writer = tf.summary.FileWriter(LOG_DIR,sess.graph)

       try:


           coord = tf.train.Coordinator()

           threads = tf.train.start_queue_runners(sess=sess, coord=coord)


           for i in range(5):

               y_,f_,t_ = sess.run([batch_year,batch_flight,batch_time])

               print sess.run(r,feed_dict={time:t_})

               #summary_str = sess.run(summary,feed_dict=feed_dict)

               #summary_writer.add_summary(summary_str,i)

               summary_writer.flush()


변경후 코드는 tt = times * 10 을 create_graph라는 함수로 뺐는데, session 시작전에 함수를 호출한다. 언뜻 보면 개념이 헷갈릴 수 있는데, time 이라는 변수는 텐서플로우의 placeholder로 값이 읽혀지는 시점이 queue_runner를 시작해야 값을 읽을 수 있는 준비 상태가 되고, 실제로 값을 큐에서 읽으려면 session을 실행하고 feed_dict를 이용하여 feeding을 해줘야 값이 채워지기 때문에, 일반적인 프로그램상으로는 session을 시작한 후에 함수를 호출해야할것 같이 생각이 되지만, 앞에서도 언급했듯이 텐서플로우에서 프로그래밍의 개념은 그래프를 다 만들어놓은 후 (데이타가 처리되는 흐름을 모두 정의해놓고) 그 다음 session을 실행하여 그래프에 데이타를 채워놓는 개념이기 때문에, session이 정의되기 전에 함수 호출등을 이용해서 그래프를 정의해야 한다.



텐서플로우 배치 처리


조대협 (http://bcho.tistory.com)


텐서플로우에서 파일에서 데이타를 읽은 후에, 배치처리로 placeholder에서 읽는  예제를 설명한다.

텐서의 shape 의 차원과 세션의 실행 시점등이 헷갈려서 시행착오가 많았기 때문에 글로 정리해놓는다.

큐와 파일처리에 대한 기본적인 내용은 아래글

  • http://bcho.tistory.com/1163

  • http://bcho.tistory.com/1165

를 참고하기 바란다.

데이타 포맷

읽어 드릴 데이타 포맷은 다음과 같다. 비행기 노선 정보에 대한 데이타로 “년도,항공사 코드, 편명"을 기록한 CSV 파일이다.

2014,VX,121

2014,WN,1873

2014,WN,2787

배치 처리 코드

이 데이타를 텐서 플로우에서 읽어서 배치로 place holder에 feeding 하는 코드 이다

먼저 read_data는 csv 파일에서 데이타를 읽어서 파싱을 한 후 각 컬럼을 year,flight,time 으로 리턴하는 함 수이다.

def read_data(file_name):

   try:

       csv_file = tf.train.string_input_producer([file_name],name='filename_queue')

       textReader = tf.TextLineReader()

       _,line = textReader.read(csv_file)

       year,flight,time = tf.decode_csv(line,record_defaults=[ [1900],[""],[0] ],field_delim=',')    

   except:

       print "Unexpected error:",sys.exc_info()[0]

       exit()

   return year,flight,time


string_input_producer를 통해서 파일명들을 큐잉해서 하나씩 읽는데,여기서는 편의상 하나의 파일만 읽도록 하였는데, 여러개의 파일을 병렬로 처리하고자 한다면, [file_name]  부분에 리스트 형으로 여러개의 파일 목록을 지정해주면 된다.

다음 각 파일을 TextReader를 이용하여 라인 단위로 읽은 후 decode_csv를 이용하여, “,”로 분리된 컬럼을 각각  읽어서 year,flight,time 에 저장하여 리턴하였다.


다음 함수는 read_data_batch 라는 함수인데, 앞에서 정의한 read_data 함수를 호출하여, 읽어드린 year,flight,time 을 배치로 묶어서 리턴하는 함수 이다.


def read_data_batch(file_name,batch_size=10):

   year,flight,time = read_data(file_name)

   batch_year,batch_flight,batch_time = tf.train.batch([year,flight,time],batch_size=batch_size)

   

   return  batch_year,batch_flight,batch_time


tf.train.batch 함수가 배치로 묶어서 리턴을 하는 함수인데, batch로 묶고자 하는 tensor 들을 인자로 준 다음에, batch_size (한번에 묶어서 리턴하고자 하는 텐서들의 개수)를 정해주면 된다.


위의 예제에서는 batch_size를 10으로 해줬기 때문에, batch_year = [ 1900,1901….,1909]  와 같은 형태로 10개의 년도를 하나의 텐서에 묶어서 리턴해준다.

즉 입력 텐서의 shape이  [x,y,z] 일 경우 tf.train.batch를 통한 출력은 [batch_size,x,y,z] 가 된다.(이 부분이 핵심)


메인 코드

자 이제 메인 코드를 보자

def main():

   

   print 'start session'

   #coornator 위에 코드가 있어야 한다

   #데이타를 집어 넣기 전에 미리 그래프가 만들어져 있어야 함.

   batch_year,batch_flight,batch_time = read_data_batch(TRAINING_FILE)

   year = tf.placeholder(tf.int32,[None,])

   flight = tf.placeholder(tf.string,[None,])

   time = tf.placeholder(tf.int32,[None,])

   

   tt = time * 10


tt = time * 10 이라는 공식을 실행하기 위해서 time 이라는 값을 읽어서 피딩하는 예제인데 먼저 read_data_batch를 이용하여 데이타를 읽는 그래프를 생성한다. 이때 주의해야할점은 이 함수를 수행한다고 해서, 바로 데이타를 읽기 시작하는 것이 아니라, 데이타의 흐름을 정의하는 그래프만 생성된다는 것을 주의하자


다음으로는 year,flight,time placeholder를 정의한다.

year,flight,time 은 0 차원의 scalar 텐서이지만, 값이 연속적으로 들어오기 때문에, [None, ] 로 정의한다.

즉  year = [1900,1901,1902,1903,.....] 형태이기 때문에 1차원 Vector 형태의 shape으로 [None, ] 로 정의한다.

Placeholder 들에 대한 정의가 끝났으면, 세션을 정의하고 데이타를 읽어드리기 위한 Queue runner를 수행한다. 앞의 과정까지 텐서 그래프를 다 그렸고, 이 그래프 값을 부어넣기 위해서, Queue runner 를 수행한 것이다.


   with tf.Session() as sess:

       try:


           coord = tf.train.Coordinator()

           threads = tf.train.start_queue_runners(sess=sess, coord=coord)


Queue runner를 실행하였기 때문에 데이타가 데이타 큐로 들어가기 시작하고, 이 큐에 들어간 데이타를 읽어드리기 위해서, 세션을 실행한다.

               y_,f_,t_ = sess.run([batch_year,batch_flight,batch_time])

               print sess.run(tt,feed_dict={time:t_})

세션을 실행하면, batch_year,batch_flight,batch_time 값을 읽어서 y_,f_,t_ 변수에 각각 집어 넣은 다음에, t_ 값을 tt 공식의 time 변수에 feeding 하여, 값을 계산한다.


모든 작업이 끝났으면 아래와 같이 Queue runner를 정지 시킨다.

           coord.request_stop()

           coord.join(threads)


다음은 앞에서 설명한 전체 코드이다.


import tensorflow as tf

import numpy as np

import sys


TRAINING_FILE = '/Users/terrycho/dev/data/flight.csv'


## read training data and label

def read_data(file_name):

   try:

       csv_file = tf.train.string_input_producer([file_name],name='filename_queue')

       textReader = tf.TextLineReader()

       _,line = textReader.read(csv_file)

       year,flight,time = tf.decode_csv(line,record_defaults=[ [1900],[""],[0] ],field_delim=',')    

   except:

       print "Unexpected error:",sys.exc_info()[0]

       exit()

   return year,flight,time


def read_data_batch(file_name,batch_size=10):

   year,flight,time = read_data(file_name)

   batch_year,batch_flight,batch_time = tf.train.batch([year,flight,time],batch_size=batch_size)

   

   return  batch_year,batch_flight,batch_time


def main():

   

   print 'start session'

   #coornator 위에 코드가 있어야 한다

   #데이타를 집어 넣기 전에 미리 그래프가 만들어져 있어야 함.

   batch_year,batch_flight,batch_time = read_data_batch(TRAINING_FILE)

   year = tf.placeholder(tf.int32,[None,])

   flight = tf.placeholder(tf.string,[None,])

   time = tf.placeholder(tf.int32,[None,])

   

   tt = time * 10


   with tf.Session() as sess:

       try:


           coord = tf.train.Coordinator()

           threads = tf.train.start_queue_runners(sess=sess, coord=coord)


           for i in range(10):

               y_,f_,t_ = sess.run([batch_year,batch_flight,batch_time])

               print sess.run(tt,feed_dict={time:t_})


           print 'stop batch'

           coord.request_stop()

           coord.join(threads)

       except:

           print "Unexpected error:", sys.exc_info()[0]


main()


다음은 실행결과이다.



딥러닝을 이용한 숫자 이미지 인식 #2/2


앞서 MNIST 데이타를 이용한 필기체 숫자를 인식하는 모델을 컨볼루셔널 네트워크 (CNN)을 이용하여 만들었다. 이번에는 이 모델을 이용해서 필기체 숫자 이미지를 인식하는 코드를 만들어 보자


조금 더 테스트를 쉽게 하기 위해서, 파이썬 주피터 노트북내에서 HTML 을 이용하여 마우스로 숫자를 그릴 수 있도록 하고, 그려진 이미지를 어떤 숫자인지 인식하도록 만들어 보겠다.



모델 로딩

먼저 앞의 예제에서 학습을한 모델을 로딩해보도록 하자.

이 코드는 주피터 노트북에서 작성할때, 모델을 학습 시키는 코드 (http://bcho.tistory.com/1156) 와 별도의 새노트북에서 구현을 하도록 한다.


코드

import tensorflow as tf

import numpy as np

import matplotlib.pyplot as plt

from tensorflow.examples.tutorials.mnist import input_data


#이미 그래프가 있을 경우 중복이 될 수 있기 때문에, 기존 그래프를 모두 리셋한다.

tf.reset_default_graph()


num_filters1 = 32


x = tf.placeholder(tf.float32, [None, 784])

x_image = tf.reshape(x, [-1,28,28,1])


#  layer 1

W_conv1 = tf.Variable(tf.truncated_normal([5,5,1,num_filters1],

                                         stddev=0.1))

h_conv1 = tf.nn.conv2d(x_image, W_conv1,

                      strides=[1,1,1,1], padding='SAME')


b_conv1 = tf.Variable(tf.constant(0.1, shape=[num_filters1]))

h_conv1_cutoff = tf.nn.relu(h_conv1 + b_conv1)


h_pool1 =tf.nn.max_pool(h_conv1_cutoff, ksize=[1,2,2,1],

                       strides=[1,2,2,1], padding='SAME')


num_filters2 = 64


# layer 2

W_conv2 = tf.Variable(

           tf.truncated_normal([5,5,num_filters1,num_filters2],

                               stddev=0.1))

h_conv2 = tf.nn.conv2d(h_pool1, W_conv2,

                      strides=[1,1,1,1], padding='SAME')


b_conv2 = tf.Variable(tf.constant(0.1, shape=[num_filters2]))

h_conv2_cutoff = tf.nn.relu(h_conv2 + b_conv2)


h_pool2 =tf.nn.max_pool(h_conv2_cutoff, ksize=[1,2,2,1],

                       strides=[1,2,2,1], padding='SAME')


# fully connected layer

h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*num_filters2])


num_units1 = 7*7*num_filters2

num_units2 = 1024


w2 = tf.Variable(tf.truncated_normal([num_units1, num_units2]))

b2 = tf.Variable(tf.constant(0.1, shape=[num_units2]))

hidden2 = tf.nn.relu(tf.matmul(h_pool2_flat, w2) + b2)


keep_prob = tf.placeholder(tf.float32)

hidden2_drop = tf.nn.dropout(hidden2, keep_prob)


w0 = tf.Variable(tf.zeros([num_units2, 10]))

b0 = tf.Variable(tf.zeros([10]))

k = tf.matmul(hidden2_drop, w0) + b0

p = tf.nn.softmax(k)


# prepare session

sess = tf.InteractiveSession()

sess.run(tf.global_variables_initializer())

saver = tf.train.Saver()

saver.restore(sess, '/Users/terrycho/anaconda/work/cnn_session')


print 'reload has been done'


그래프 구현

코드를 살펴보면, #prepare session 부분 전까지는 이전 코드에서의 그래프를 정의하는 부분과 동일하다. 이 코드는 우리가 만든 컨볼루셔널 네트워크를 복원하는 부분이다.


변수 데이타 로딩

그래프의 복원이 끝나면, 저장한 세션의 값을 다시 로딩해서 학습된 W와 b값들을 다시 로딩한다.


# prepare session

sess = tf.InteractiveSession()

sess.run(tf.global_variables_initializer())

saver = tf.train.Saver()

saver.restore(sess, '/Users/terrycho/anaconda/work/cnn_session')


이때 saver.restore 부분에서 앞의 예제에서 저장한 세션의 이름을 지정해준다.

HTML을 이용한 숫자 입력

그래프와 모델 복원이 끝났으면 이 모델을 이용하여, 숫자를 인식해본다.

테스트하기 편리하게 HTML로 마우스로 숫자를 그릴 수 있는 화면을 만들어보겠다.

주피터 노트북에서 새로운 Cell에 아래와 같은 내용을 입력한다.


코드

input_form = """

<table>

<td style="border-style: none;">

<div style="border: solid 2px #666; width: 143px; height: 144px;">

<canvas width="140" height="140"></canvas>

</div></td>

<td style="border-style: none;">

<button onclick="clear_value()">Clear</button>

</td>

</table>

"""


javascript = """

<script type="text/Javascript">

   var pixels = [];

   for (var i = 0; i < 28*28; i++) pixels[i] = 0

   var click = 0;


   var canvas = document.querySelector("canvas");

   canvas.addEventListener("mousemove", function(e){

       if (e.buttons == 1) {

           click = 1;

           canvas.getContext("2d").fillStyle = "rgb(0,0,0)";

           canvas.getContext("2d").fillRect(e.offsetX, e.offsetY, 8, 8);

           x = Math.floor(e.offsetY * 0.2)

           y = Math.floor(e.offsetX * 0.2) + 1

           for (var dy = 0; dy < 2; dy++){

               for (var dx = 0; dx < 2; dx++){

                   if ((x + dx < 28) && (y + dy < 28)){

                       pixels[(y+dy)+(x+dx)*28] = 1

                   }

               }

           }

       } else {

           if (click == 1) set_value()

           click = 0;

       }

   });

   

   function set_value(){

       var result = ""

       for (var i = 0; i < 28*28; i++) result += pixels[i] + ","

       var kernel = IPython.notebook.kernel;

       kernel.execute("image = [" + result + "]");

   }

   

   function clear_value(){

       canvas.getContext("2d").fillStyle = "rgb(255,255,255)";

       canvas.getContext("2d").fillRect(0, 0, 140, 140);

       for (var i = 0; i < 28*28; i++) pixels[i] = 0

   }

</script>

"""


다음 새로운 셀에서, 다음 코드를 입력하여, 앞서 코딩한 HTML 파일을 실행할 수 있도록 한다.


from IPython.display import HTML

HTML(input_form + javascript)


이제 앞에서 만든 두 셀을 실행시켜 보면 다음과 같이 HTML 기반으로 마우스를 이용하여 숫자를 입력할 수 있는 박스가 나오는것을 확인할 수 있다.



입력값 판정

앞의 HTML에서 그린 이미지는 앞의 코드의 set_value라는 함수에 의해서, image 라는 변수로 784 크기의 벡터에 저장된다. 이 값을 이용하여, 이 그림이 어떤 숫자인지를 앞서 만든 모델을 이용해서 예측을 해본다.


코드


p_val = sess.run(p, feed_dict={x:[image], keep_prob:1.0})


fig = plt.figure(figsize=(4,2))

pred = p_val[0]

subplot = fig.add_subplot(1,1,1)

subplot.set_xticks(range(10))

subplot.set_xlim(-0.5,9.5)

subplot.set_ylim(0,1)

subplot.bar(range(10), pred, align='center')

plt.show()

예측

예측을 하는 방법은 쉽다. 이미지 데이타가 image 라는 변수에 들어가 있기 때문에, 어떤 숫자인지에 대한 확률을 나타내는 p 의 값을 구하면 된다.


p_val = sess.run(p, feed_dict={x:[image], keep_prob:1.0})


를 이용하여 x에 image를 넣고, 그리고 dropout 비율을 0%로 하기 위해서 keep_prob를 1.0 (100%)로 한다. (예측이기 때문에 당연히 dropout은 필요하지 않다.)

이렇게 하면 이 이미지가 어떤 숫자인지에 대한 확률이 p에 저장된다.

그래프로 표현

그러면 이 p의 값을 찍어 보자


fig = plt.figure(figsize=(4,2))

pred = p_val[0]

subplot = fig.add_subplot(1,1,1)

subplot.set_xticks(range(10))

subplot.set_xlim(-0.5,9.5)

subplot.set_ylim(0,1)

subplot.bar(range(10), pred, align='center')

plt.show()


그래프를 이용하여 0~9 까지의 숫자 (가로축)일 확률을 0.0~1.0 까지 (세로축)으로 출력하게 된다.

다음은 위에서 입력한 숫자 “4”를 인식한 결과이다.



(보너스) 첫번째 컨볼루셔널 계층 결과 출력

컨볼루셔널 네트워크를 학습시키다 보면 종종 컨볼루셔널 계층을 통과하여 추출된 특징 이미지들이 어떤 모양을 가지고 있는지를 확인하고 싶을때가 있다. 그래서 각 필터를 통과한 값을 이미지로 출력하여 확인하고는 하는데, 여기서는 이렇게 각 필터를 통과하여 인식된 특징이 어떤 모양인지를 출력하는 방법을 소개한다.


아래는 우리가 만든 네트워크 중에서 첫번째 컨볼루셔널 필터를 통과한 결과 h_conv1과, 그리고 이 결과에 bias 값을 더하고 활성화 함수인 Relu를 적용한 결과를 출력하는 예제이다.


코드


conv1_vals, cutoff1_vals = sess.run(

   [h_conv1, h_conv1_cutoff], feed_dict={x:[image], keep_prob:1.0})


fig = plt.figure(figsize=(16,4))


for f in range(num_filters1):

   subplot = fig.add_subplot(4, 16, f+1)

   subplot.set_xticks([])

   subplot.set_yticks([])

   subplot.imshow(conv1_vals[0,:,:,f],

                  cmap=plt.cm.gray_r, interpolation='nearest')

plt.show()


x에 image를 입력하고, dropout을 없이 모든 네트워크를 통과하도록 keep_prob:1.0으로 주고, 첫번째 컨볼루셔널 필터를 통과한 값 h_conv1 과, 이 값에 bias와 Relu를 적용한 값 h_conv1_cutoff를 계산하였다.

conv1_vals, cutoff1_vals = sess.run(

   [h_conv1, h_conv1_cutoff], feed_dict={x:[image], keep_prob:1.0})


첫번째 필터는 총 32개로 구성되어 있기 때문에, 32개의 결과값을 imshow 함수를 이용하여 흑백으로 출력하였다.




다음은 bias와 Relu를 통과한 값인 h_conv_cutoff를 출력하는 예제이다. 위의 코드와 동일하며 subplot.imgshow에서 전달해주는 인자만 conv1_vals → cutoff1_vals로 변경되었다.


코드


fig = plt.figure(figsize=(16,4))


for f in range(num_filters1):

   subplot = fig.add_subplot(4, 16, f+1)

   subplot.set_xticks([])

   subplot.set_yticks([])

   subplot.imshow(cutoff1_vals[0,:,:,f],

                  cmap=plt.cm.gray_r, interpolation='nearest')

   

plt.show()


출력 결과는 다음과 같다



이제까지 컨볼루셔널 네트워크를 이용한 이미지 인식을 텐서플로우로 구현하는 방법을 MNIST(필기체 숫자 데이타)를 이용하여 구현하였다.


실제로 이미지를 인식하려면 전체적인 흐름은 같지만, 이미지를 전/후처리 해내야 하고 또한 한대의 머신이 아닌 여러대의 머신과 GPU와 같은 하드웨어 장비를 사용한다. 다음 글에서는 MNIST가 아니라 실제 칼라 이미지를 인식하는 방법에 대해서 데이타 전처리에서 부터 서비스까지 전체 과정에 대해서 설명하도록 하겠다.


예제 코드 : https://github.com/bwcho75/tensorflowML/blob/master/MNIST_CNN_Prediction.ipynb


텐서플로우-#1 자료형의 이해

빅데이타/머신러닝 | 2016.12.09 22:42 | Posted by 조대협

텐서플로우-#1 자료형의 이해


조대협 (http://bcho.tistory.com)


딥러닝에 대한 대략적인 개념을 익히고 실제로 코딩을 해보려고 하니, 모 하나를 할때 마다 탁탁 막힌다. 파이썬이니 괜찮겠지 했는데, (사실 파이썬도 다 까먹어서 헷갈린다.) 이건 라이브러리로 도배가 되어 있다.

당연히 텐서플로우 프레임웍은 이해를 해야 하고, 데이타를 정재하고 시각화 하는데, numpy,pandas와 같은 추가적인 프레임웍에 대한 이해가 필요하다.


node.js 시작했을때도 자바스크립트 때문에 많이 헤매고 몇달이 지난후에야 어느정도 이해하게 되었는데, 역시나 차근차근 기초 부터 살펴봐야 하지 않나 싶다.


텐서 플로우에 대해 공부한 내용들을 하나씩 정리할 예정인데, 이 컨텐츠들은 유투브의 이찬우님의 강의를 기반으로 정리하였다. 무엇보다 한글이고 개념을 쉽게 풀어서 정리해주시기 때문에, 왠만한 교재 보다 났다.

https://www.youtube.com/watch?v=a74pFg8paVc


텐서플로우 환경 설정

텐서 플로우 환경을 설정 하는 방법은 쉽지 않다. 텐서플로우 뿐 아니라, 여러 파이썬 버전과 그에 맞는 라이브러리도 함께 설정해야 하기 때문에 여간 까다로운게 아닌데, 텐서플로우 환경은 크게 대략 두 가지 환경으로 쉽게 설정이 가능하다.

구글 데이타랩

첫번째 방법은 구글에서 주피터 노트북을 도커로 패키징해놓은 패키지를 이용하는 방법이다. 도커 패키지안에, numpy,pandas,matplotlib,tensorflow,python 등 텐서플로우 개발에 필요한 모든 환경이 패키징 되어 있다. 데이타 랩 설치 방법은 http://bcho.tistory.com/1134 링크를 참고하면 된다.

도커 런타임이 설치되어 있다면, 데이타랩 환경 설정은 10분이면 충분하다.

아나콘다

다음 방법은 일반적으로 가장 많이 사용하는 방법인데, 파이썬 수학관련 라이브러리를 패키징해놓은 아나콘다를 이용하는 방법이 있다. 자세한 환경 설정 방법은 https://www.tensorflow.org/versions/r0.12/get_started/os_setup.html#anaconda-installation 를 참고하기 바란다. 아나콘다를 설치해놓고, tensorflow 환경(environment)를 정의한 후에, 주피터 노트북을 설치하면 된다. http://stackoverflow.com/questions/37061089/trouble-with-tensorflow-in-jupyter-notebook 참고


Tensorflow 환경을 만든 후에,

$ source activate tensorflow

를 실행해서 텐서 플로우 환경으로 전환한후, 아래와 같이 ipython 을 설치한후에, 주피터 (jupyter) 노트북을 설치하면 된다.

(tensorflow) username$ conda install ipython
(tensorflow) username$ pip install jupyter #(use pip3 for python3)


아나콘다 기반의 텐서플로우 환경 설정은 나중에 시간이 될때 다른 글을 통해서 다시 설명하도록 하겠다.

텐서플로우의 자료형

텐서플로우는 뉴럴네트워크에 최적화되어 있는 개발 프레임웍이기 때문에, 그 자료형과, 실행 방식이 약간 일반적인 프로그래밍 방식과 상의하다. 그래서 삽질을 많이 했다.


상수형 (Constant)

상수형은 말 그대로 상수를 저장하는 데이타 형이다.

  • tf.constant(value, dtype=None, shape=None, name='Const', verify_shape=False)

와 같은 형태로 정의 된다. 각 정의되는 내용을 보면

  • value : 상수의 값이다.

  • dtype : 상수의 데이타형이다. tf.float32와 같이 실수,정수등의 데이타 타입을 정의한다.

  • shape : 행렬의 차원을 정의한다. shape=[3,3]으로 정의해주면, 이 상수는 3x3 행렬을 저장하게 된다.

  • name : name은 이 상수의 이름을 정의한다. name에 대해서는 나중에 좀 더 자세하게 설명하도록 하겠다.

간단한 예제를 하나 보자.

a,b,c 상수에, 각각 5,10,2 의 값을 넣은 후에, d=a*b+c 를 계산해서 계산 결과 d를 출력하려고 한다.

import tensorflow as tf


a = tf.constant([5],dtype=tf.float32)

b = tf.constant([10],dtype=tf.float32)

c = tf.constant([2],dtype=tf.float32)


d = a*b+c


print d

그런데, 막상 실행해보면, a*b+c의 값이 아니라 다음과 같이 Tensor… 라는 문자열이 출력된다.


Tensor("add_8:0", shape=(1,), dtype=float32)

그래프와 세션의 개념

먼저 그래프와 세션이라는 개념을 이해해야 텐서플로우의 프로그래밍 모델을 이해할 수 있다.

위의 d=a*b+c 에서 d 역시 계산을 수행하는 것이 아니라 다음과 같이 a*b+c 그래프를 정의하는 것이다.


실제로 값을 뽑아내려면, 이 정의된 그래프에 a,b,c 값을 넣어서 실행해야 하는데, 세션 (Session)을 생성하여,  그래프를 실행해야 한다. 세션은 그래프를 인자로 받아서 실행을 해주는 일종의 러너(Runner)라고 생각하면 된다.


자 그러면 위의 코드를 수정해보자


import tensorflow as tf


a = tf.constant([5],dtype=tf.float32)

b = tf.constant([10],dtype=tf.float32)

c = tf.constant([2],dtype=tf.float32)


d = a*b+c


sess = tf.Session()

result = sess.run(d)

print result



tf.Session()을 통하여 세션을 생성하고, 이 세션에 그래프 d를 실행하도록 sess.run(d)를 실행한다

이 그래프의 실행결과는 리턴값으로 result에 저장이 되고, 출력을 해보면 다음과 같이 정상적으로 52라는 값이 나오는 것을 볼 수 있다.


플레이스 홀더 (Placeholder)

자아 이제 상수의 개념을 알았으면, 이제는 플레이스 홀더에 대해서 알아보자.

y = x * 2 를 그래프를 통해서 실행한다고 하자. 입력값으로는 1,2,3,4,5를 넣고, 출력은 2,4,6,8,10을 기대한다고 하자. 이렇게 여러 입력값을 그래프에서 넣는 경우는 머신러닝에서 y=W*x + b 와 같은 그래프가 있다고 할 때, x는 학습을 위한 데이타가 된다.

즉 지금 살펴보고자 하는 데이타 타입은 학습을 위한 학습용 데이타를 위한 데이타 타입이다.


y=x*2를 정의하면 내부적으로 다음과 같은 그래프가 된다.


그러면, x에는 값을 1,2,3,4,5를 넣어서 결과값을 그래프를 통해서 계산해 내야한다. 개념적으로 보면 다음과 같다.



이렇게 학습용 데이타를 담는 그릇을 플레이스홀더(placeholder)라고 한다.

플레이스홀더에 대해서 알아보면, 플레이스 홀더의 위의 그래프에서 x 즉 입력값을 저장하는 일종의 통(버킷)이다.

tf.placeholder(dtype,shape,name)

으로 정의된다.

플레이스 홀더 정의에 사용되는 변수들을 보면

  • dtype : 플레이스홀더에 저장되는 데이타형이다. tf.float32와 같이 실수,정수등의 데이타 타입을 정의한다.

  • shape : 행렬의 차원을 정의한다. shapre=[3,3]으로 정의해주면, 이 플레이스홀더는 3x3 행렬을 저장하게 된다.

  • name : name은 이 플레이스 홀더의 이름을 정의한다. name에 대해서는 나중에 좀 더 자세하게 설명하도록 하겠다.


그러면 이 x에 학습용 데이타를 어떻게 넣을 것인가? 이를 피딩(feeding)이라고 한다.

다음 예제를 보자


import tensorflow as tf


input_data = [1,2,3,4,5]

x = tf.placeholder(dtype=tf.float32)

y = x * 2


sess = tf.Session()

result = sess.run(y,feed_dict={x:input_data})


print result


처음 input_data=[1,2,3,4,5]으로 정의하고

다음으로 x=tf.placeholder(dtype=tf.float32) 를 이용하여, x를 float32 데이타형을 가지는 플레이스 홀더로 정의하다. shape은 편의상 생략하였다.

그리고 y=x * 2 로 그래프를 정의하였다.


세션이 실행될때, x라는 통에 값을 하나씩 집어 넣는데, (앞에서도 말했듯이 이를 피딩이라고 한다.)

sess.run(y,feed_dict={x:input_data}) 와 같이 세션을 통해서 그래프를 실행할 때, feed_dict 변수를 이용해서 플레이스홀더 x에, input_data를 피드하면, 세션에 의해서 그래프가 실행되면서 x는 feed_dict에 의해서 정해진 피드 데이타 [1,2,3,4,5]를 하나씩 읽어서 실행한다.


변수형 (Variable)

마지막 데이타형은 변수형으로,

y=W*x+b 라는 학습용 가설이 있을때, x가 입력데이타 였다면, W와 b는 학습을 통해서 구해야 하는 값이 된다.  이를 변수(Variable)이라고 하는데, 변수형은 Variable 형의 객체로 생성이 된다.


  • tf.Variable.__init__(initial_value=None, trainable=True, collections=None, validate_shape=True, caching_device=None, name=None, variable_def=None, dtype=None, expected_shape=None, import_scope=None)


변수형에 값을 넣는 것은 다음과 같이 한다.


var = tf.Variable([1,2,3,4,5], dtype=tf.float32)


자 그러면 값을 넣어보고 코드를 실행해보자


import tensorflow as tf


input_data = [1,2,3,4,5]

x = tf.placeholder(dtype=tf.float32)

W = tf.Variable([2],dtype=tf.float32)

y = W*x


sess = tf.Session()

result = sess.run(y,feed_dict={x:input_data})


print result


우리가 기대하는 결과는 다음과 같다. y=W*x와 같은 그래프를 가지고,


x는 [1,2,3,4,5] 값을 피딩하면서, 변수 W에 지정된 2를 곱해서 결과를 내기를 바란다.

그렇지만 코드를 실행해보면 다음과 같이 에러가 출력되는 것을 확인할 수 있다.



이유는 텐서플로우에서 변수형은 그래프를 실행하기 전에 초기화를 해줘야 그 값이 변수에 지정이 된다.


세션을 초기화 하는 순간 변수 W에 그 값이 지정되는데, 초기화를 하는 방법은 다음과 같이 변수들을 global_variables_initializer() 를 이용해서 초기화 한후, 초기화된 결과를 세션에 전달해 줘야 한다.


init = tf.global_variables_initializer()

sess.run(init)


그러면 초기화를 추가한 코드를 보자


import tensorflow as tf


input_data = [1,2,3,4,5]

x = tf.placeholder(dtype=tf.float32)

W = tf.Variable([2],dtype=tf.float32)

y = W*x


sess = tf.Session()

init = tf.global_variables_initializer()

sess.run(init)

result = sess.run(y,feed_dict={x:input_data})


print result


초기화를 수행한 후, 코드를 수행해보면 다음과 같이 우리가 기대했던 결과가 출력됨을 확인할 수 있다.



텐서플로우를 처음 시작할때, Optimizer나 모델등에 대해 이해하는 것도 중요하지만, “데이타를 가지고 학습을 시켜서 적정한 값을 찾는다" 라는 머신러닝 학습 모델의 특성상, 모델을 그래프로 정의하고, 세션을 만들어서 그래프를 실행하고, 세션이 실행될때 그래프에 동적으로 값을 넣어가면서 (피딩) 실행한 다는 기본 개념을 잘 이해해야, 텐서플로우 프로그래밍을 제대로 시작할 수 있다.


파이어베이스 애널러틱스를 이용한 모바일 데이타 분석

#2-분석 지표와 대쉬 보드 이해하기


조대협 (http://bcho.tistory.com)


파이어베이스 애널러틱스로 지표를 수집하게 되면, 몬가 아름다워(?) 보이는 대쉬 보드와 그래프들을 볼 수 있다. 그러나 정작 각 그래프의 항목과 수치가 무엇을 의미하는지를 이해하지 못한다면 무용 지물이나 다름없다.


비단 파이어베이스 애널러틱스 뿐 아니라, 일반적인 데이타 분석에서도 많이 겪는 실수중에 하나인데, 이번에는 파이어베이스 애널러틱스에 의해서 분석되어 리포트로 제공되는 각종 지표와 이와 연관된 이벤트들에 대해서 알아보도록 한다.

대쉬 보드

파이어베이스 애널러틱스를 사용하게 되면 리포트는 대쉬보드를 통하여 출력되게 된다. 대쉬 보드는 대략 아래와 같이 생겼는데, 각 항목을 살펴보도록 하자



출처 https://support.google.com/firebase/answer/6317517?hl=en&ref_topic=6317489

기준 시간

분석 지표에 대한 이해를 하기 위해서는 먼저 기준 시간에 대한 이해를 할 필요가 있다. 파이어베이스 애널러틱스 콘솔의 우측 상단의 보면 분석 기간을 선택할 수 있다. 분석 기간은 오늘, 어제, 이번주, 지난 7일, 지난 30일 등 미리 정해진 기간이나 Custom을 이용하여, 기간을 정의할 수 있다.


1. Active User (활성 사용자수)

가장 처음에 나오는 지표는 활성 사용자 수 이다. 가장 많이 보는 지표중의 하나인데, 일,월,주별 방문자 수 이다.


  • Monthly Active User (MAU:월별 활성 사용자 수)
    그래프의 X축의 날짜에서 부터 부터 전 30일까지의 앱을 사용한 총 일일 사용자 수.

  • Weekly Active User (WAU:주별 활성 사용자 수)
    그래프의 X축의 날짜에서 부터 전 7일 까지 앱을 사용한 총 일일 사용자의 수

  • Daily Active User (DAU : 주별 활성 사용자 수)
    그래프의 X축 날짜의 앱을 사용한 일일 사용자의 수


위의 그래프를 보면 WAU와 DAU는 수평을 그리고 있는데, 반하여 MAU가 올라가고 있음을 볼 수 있다. 이 그래프는 파이어베이스 애널러틱스를 설치한지 얼마 되지 않는 기간에 뽑은 리포트인데, DAU는 일정하기 때문에, MAU는 누적되서 그래프가 상승 곡선을 띄게 되는 것이다.

예를 들어 8월1일에 설치했다고 했을때, 8월2일의 MAU는 7월3일~8월2일 DAU의 합이 되는데, 8월 1일에 설치를 했기 때문에 7월3일~7월30일까지의 데이타는 없다. 8월 30일의 MAU는 8월1일~8월30일까지 합이고, 8월1~30일까지는 데이타가 있기 때문에 누적되서 상승 곡선을 그리게 된다.

2. Average Revenue (평균 수익)

다음 지표는 수익 지표이다. 크게 ARPU와 ARPPU로 표현되는데 그 내용은 다음과 같다.

  • ARPU (Average revenue per User)
    사용자별 수익률로, 전체 수익을 전체 사용자 수로 나눠서 계산한다.

  • ARPPU (Average revenue per purchased user)
    유료 사용자별 수익률로, 전체수익을 비용을 지불한 사용자로 나눠서 계산한다.

전체 서비스가 유료가 아닌 이상, 커머스의 경우 일부 사용자만 물건을 구매하거나, 게임이나 서비스 앱인 경우에는 일부 사용자만 인앱구매등을 통해서 비용을 지불하기 때문에 다른 두개의 지표가 나온다.

ARPU는 서비스에서 사용자가 증가하는 당 수익률이 어떻게 올라가는지를 알 수 있고, ARPPU는 유료 사용자당 얼마의 금액을 사용하는지를 이해할 수 있다.


이 지표는 파이어베이스 애널러틱스에서  ecommerce_purchase (쇼핑몰 이벤트 중, 구매 이벤트)와 in_app_purchase (일반 이벤트중 인앱 구매) 이벤트에 의해서 추적되기 때문에, ARPU와 ARPPU를 구하고 싶으면, 상품구매나 인앱 구매가 발생하였을때, 위의 이벤트를 통해서 파이어베이스 애널러틱스에 이벤트를 로깅해줘야 한다.  


3. first_open attribution (앱실행 빈도)

다음 지표는 첫 앱 실행을 추적하는 지표이다.

기준 시간 기간 동안 인스톨 또는 재 인스톨이 된후, 처음으로 앱이 실행된 횟수를 추적하는 지표이다.

이 지표는 다양한 의미를 가지고 있는데, 앱 다운로드가 캠페인등을 통해서 많이 일어났다고 하더라도, 앱을 한번도 실행을 해보지 않고 삭제하는 경우도 많기 때문에, 앱 다운로드 대비, 얼마나 많은 사용자가 실제로 앱을 실행했는 가를 추적할 수 있다.

앱 다운로드 횟수는 구글 플레이 스토어나 애플 앱스토어의 사용자 콘솔에서 그 값을 추적할 수 있다.


또한 “NETWORK SETTING”에서 광고 서비스 네트워크를 연동할 수 있는데, 광고 네트워크를 연동하게 되면 앱의 설치가 사용자가 앱스토어에서 그냥 자발적으로 설치를 한것인지 아니면 광고 네트워크의 특정 광고 캠페인을 통해서 인입된 사용자인지를 판단할 수 있다.



<그림 광고 네트워크를 연동하는 화면 >


이를 통해서, 광고 마케팅의 효율과, 성과를 측정하여 효율적인 광고 집행이 가능하다.

앱 첫실행을 기록하는 first_open 이벤트는 개발자가 별도로 코드 상에 정의하지 않더라도 자동으로 로깅 된다.

아래 예제를 보자, 광고 네트워크를 통하지 않고, 앱을 처음 사용한 것이 150K 정도 되고, 다음은 구글을 통해서 들어온 비중이 38K  정도가 된다.



맨뒤에, LTV 라는 수치가 있는데, LTV는 Life Time Value의 약자로 사용자가 앱을 설치 한 후, 초기 120일 동안에 일으킨 매출의 수의 총합이다. 매출은 ARPU와 같이   ecommerce_purchase (쇼핑몰 이벤트 중, 구매 이벤트)와 in_app_purchase (일반 이벤트중 인앱 구매) 이벤트에 의해서 추적된다.

이를 통해서 광고 네트워크별로 얼마만큼의 사용자가 들어오고, 유입된 사용자가 발생 시킨 매출을 추적하여 광고의 효율을 측정할 수 있다.


여기서 포스트백 (PostBack)이라는 기능을 잠깐 짚고 넘어갈 필요가 있는데, 쇼핑몰에서 광고 네트워크를 통해서 광고를 집행하고 있다고 하자, 사용자가 호텔 예약을 하고 싶어하는 니즈를 파악하고 사용자에게 호텔 예약 광고를 계속 내보냈다. 광고를 통해서 사용자는 호텔을 예약했다고 하자. 그렇다면 이제 더이상 해당 사용자에게 호텔 광고가 계속 나가면 안된다. (이미 팔았기 때문에) 이를 막기 위해서 광고 네트워크에 해당 물건을 사용자가 구매했으니, 더 이상 같은 광고를 내보내지 말라고 알려줘야 한다. 이를 포스트 백(Postback)이라고 한다. 파이어베이스 애널러틱스에서 포스트백을 설정하는 방법은 https://support.google.com/firebase/answer/6317518?hl=en&utm_id=ad#postbacks 를 참고하기 바란다.

4. Retention cohort (사용자 잔존율 코호트 분석)

다음 지표는 사용자 잔존율을 코호트 분석을 통해서 분석해낸 결과로, 사용자가 처음 앱을 사용한 후 얼마나 많은 사용자가 지속적으로 남아 있느냐를 나타내는 중요한 지표이다. 주 단위 잔존율을 기준으로 통계를 잡아주는데, 잔존 사용자가 많을 수록, 그래프가 더 진하게 표시 되는데, 다음 예제를 보면, 7월17일~7월23일에 가입한 사용자는 총 19481명으로 첫주에는 100% 사용자가 잔존하였으나, 1주 후에는 23.5%만 남았고, 2 주후에는 12.2%만 남았다가 5주후에는 6.4%만 남았다.

7월31~8월6일에 가입한 사용자의 경우 1주차에 23.7%가 남아 있어서 다른 주 대비 잔존율이 높아서 조금 더 진한 색깔이 그래프로 표현되었다.



5. User engagement (사용자 활동 지표)

사용자 활동 지표란, 사용자들이 기간동안 얼마나 앱을 사용했느냐에 대한 기간과 횟수등을 표현하는 지표들이다. 아래 그래프 예제로 설명하면




  • Daily engagement (총 사용시간)
    통계 기간 (기준 시간 기간) 동안 모든 사용자들이 앱을 사용한 총 시간의 합이다. 위의 예에서는 1년 34일 14시간을 사용한것으로 집게 되었다.

  • Daily engagement per user (사용자당 평균 사용 시간)
    통계 기간중, 사용자 1인당 평균 사용시간이다. Daily engagement를 그 기간 동안 총 활성 사용자 수로 나눈 값이다.

  • Session per user (사용자당 평균 세션 수 )
    사용자당 평균 세션 수 인데, 세션은 사용자가 기간동안 앱을 사용한 횟수로 보면 되다. 위의 예제에서는 사용자당 평균 3.7 회 정도 사용하였다.

  • Avg. session duration (사용자당 평균 세션 길이)
    사용자당 세션의 길이로, 한번 사용할때 평균 얼마 정도의 시간을 사용하느냐인데, 여기서는 사용자당 한번 사용에 7분 8초 정도를 사용한것으로 집게 되었다.


이런 통계 분석에서 주의할점은 이는 어디까지나 평균 값일 뿐이다. 특정 사용자는 기간동안 평균값이 3.7회가 넘는 10회 20회를 사용할 수 도 있고, 어떤 사용자 층은 한번 밖에 사용하지 않을 수 도 있다. 일반적으로 모바일 서비스 앱은 그 사용횟수나 사용 시간에 대한 분포가 특정 사용자군 (헤비유저)에게 몰리는 경향이 있기 때문에, 이러한 평균 지표보다는 정규 분포형의 지표를 따라서 분석하는 것이 조금 더 정확한데, 이를 위해서는 파이어베이스 애널러틱스의 지표만으로는 불가능하고, 원본 데이타를 기반으로 분석을 할 필요가 있다. 이를 위해서 원본 데이타를 빅쿼리에 저장한 후 분석하는 것이 좋은데, 이 방법은 나중에 다시 설명하도록 하겠다.

6. In-App purchase (인앱 구매)

이 지표는 인앱 구매에 대한 지표로, in_app_purchase 이벤트에 의해서 수집된 정보를 기반으로 통계를 계산한다. 총 얼마 만큼의 사용자가, 인앱 구매를 했는지를 출력하고, 이를 통해서 발생된 매출을 출력한다.

아울러 아래 그림과 같이 최고 매출을 일으킨 인앱 구매 상품들을 구매 횟수와 총 매출액을 통계로 표시해준다.



아래의 “VIEW IN-APP PURCHASE DETAILS” 탭을 클릭하면, 모든 인앱 상품의 매출 정보와 판매 추이,  사용자 연령대별 매출 발생 비중등 자세한 정보를 볼 수 있다.


<그림. 인앱 구매 이벤트 집게 화면에서 상세 화면중 성별 및 연령 별 구매 비율 >


7. App version (앱 버전)

통계 기간 동안 모든 사용자가 사용한 앱 버전에 대한 통계를 보여준다. 상위 3개의 버전을 보여주고, 나머지는 Others로 묶어서 통계로 보여준다.


앱 버전 역시 모바일 서비스에서 매우 중요한 지표중의 하나인데, 신기능이나 신규 컨텐츠가 올라가더라도 버전이 옛날 버전이 많이 깔려 있을 경우 신규 기능이나 컨텐츠가 동작하지 않을 수 도 있기 때문에, 얼마나 사용자들이 새 버전으로 업데이트했는지 추적하는 것이 중요한 지표가 되며, 아울러 경우에 따라서 예전 버전이 많을 경우에는 강제 업데이트를 해야 하는 경우도 있기 때문에, 앱 버전에 대한 추적 역시 매우 중요한 지표로 작용하낟.

8. Devices (디바이스)

통계 기간동안에 사용자가 앱을 사용하는데 사용한 주요 디바이스명과, OS 버전에 대한 통계이다.

디바이스명은 테스트 환경을 만들때 사용자들이 주로 어떤 디바이스를 사용하는지를 알면 테스트 디바이스를 준비하기가 편리하기 때문이고, OS version의 경우, 낮은 버전의 OS에서는 특정 SDK나 기능이 작동하지 않을 수 있기 때문에 앱 개발시 어느 OS 버전 부터 지원을 해야 할지, 그리고 사용 빈도가 낮은 OS는 언제 지원을 중단할 수 있을지등을 결정할 수 있는 지표로 활용이 가능하다.


9. Location(위치)

이해는 쉽지만 가장 중요한 지표중의 하나이다. 해당 기간동안 주로 어느 국가에서 앱이 많이 사용되었는 가를 리포팅 해주는 지표이다.


국내나 특정 국가 한정 서비스인 경우가 아닌 글로벌 서비스인 경우 서비스가 어느 나라에서 인기가 있는 가에 따라서, 그 나라에 맞도록 앱을 현지화 하거나, 앱에 대한 마케팅 자원등을 선택과 집중할 수 있다.

10. Demographics (데모그래픽 정보)

데모 그래픽 정보는 사용자의 연령과 성별등을 나타내는 정보이다.

이를 통하여 앱 사용자가 누구인지를 파악할 수 있고, 이를 기반으로 앱 서비스를 타케팅할 수 있는 대상을 식별하여, 제공할 컨텐츠, 마케팅 캠페인 대상등을 정할 수 있다.  



11. Interest (사용자 흥미)

마지막으로 이 앱 서비스를 사용하는 사용자가 어떤 흥미를 가지고 있는지를 분석 해주는 기능인데,

이러한 모바일 분석 플랫폼을 무료로 제공하는 서비스 제공자는 구글뿐아니라 야후, 트위터와 같이 광고를 통해서 수익을 창출하는 경우가 많다. 이러한 사업자등은 자사의 서비스에서 사용자들이 어떤 서비스나 어떤 컨텐츠를 선호 하는지를 분석한 후에, 이를 기반으로 모바일 데이타 분석 플랫폼을 사용하는 앱 개발사들의 사용자들이 어떤 컨텐츠나 서비스를 선호하는지를 추적 분석해주는데, 이것이 Interest 분석이다.


위의 그림과 같이 이 앱을 사용하는 사용자들은 TV나 온라인 비디오에 관심이 많은 사용자들이 7.6%, 그리고 음악에 관심이 있는 사용자들이 6.7%, 카메라나 전자 제품에 관심 있는 사용자들이 3.6% 정도이다.

이를 통해서 앱 사용자들을 대상으로 한 타겟 광고나 서비스 개선등에 활용할 수 있다.


지금까지 간략하게나마 파이어베이스 애널러틱스 대쉬보드의 주요 지표에 대해서 설명하였다.

여기에 나오는 지표들은 파이어베이스뿐 아니라 일반적인 모바일 앱 서비스 분석 지표로도 사용되는 만큼, 잘 이해해놓으면 모바일 서비스 빅데이타 분석에 유용하게 활용할 수 있다.


다음 글에서는 파이어베이스 애널러틱스의 주요 이벤트들에 대해서 설명하도록 하겠다.