머신 러닝(Machine Learning) 알고리즘들

|

Machine Learning 알고리즘들에 대해서 알아보도록 하겠습니다.

여기서는 몇 가지 대표적인 알고리즘들의 간단한 소개만 포스팅하도록 하겠습니다. 좀 더 자세한 정보를 원하시는 분들은

와 같은 사이트를 참조하시면 좀 더 다양하고 자세하게 알 수 있습니다.


Linear Regression

Image

선형 회귀라고도 불리는 Linear Regression은 위의 그림과 같이 모든 데이터들이 선형적인 분포 형태를 갖고 있다는 가정으로부터 시작하는 알고리즘입니다. 자주 사용되고, 비교적 간단한 구조로 되어 있어 속도도 빠르지만 데이터들이 선형 분포를 이루는 경우가 그리 많지 않기에 정확도면에서는 떨어질 수 밖에 없는 형태입니다.


Logistic Regression

Image

Logistic Regression는 위에서 언급한 Linear Regression이 가진 문제 때문에 나타난 알고리즘입니다. 모든 데이터가 선형 특성을 가지는게 아니기 때문에 여러 개의 클래스를 가진 Logistic Regression이 등장하게 되었고, 위 이미지는 2개의 클래스를 가진 Logistic Regression 알고리즘 예시입니다.


Trees, forests, and jungles

Image

‘결정 트리’라고 해서 많이 알려진 Decision Tree나, Decision Forest, Decision Jungle이다. 전체 영역을 같은 레이블(label)을 갖는 데이터들의 영역들로 나누어 관리하는 알고리즘입니다.

  • Decision Forests는 많은 양의 메모리를 필요로 합니다.
  • Decision Jungle은 메모리 소모는 줄어들지만, 학습 시간이 오래 걸립니다.


Neural Networks

Image

사람의 두뇌 구조에서 영감을 얻어서 만들어진 학습 기술입니다. 무제한적인 다양성을 갖게 해주지만, 상당히 복잡하고 필요로 하는 Cost 또한 높은 단점이 있습니다. 학습 시간도 오래 걸리며, 다른 알고리즘들에 비해 필요로 하는 파라메터(parameter)도 많아서 사용하기 어려운 점이 있지만, 잠재력은 무궁무진하다고 볼 수 있습니다.


Support Vector Machine

Image

각 데이터를 구분하는 경계를 찾는 방식입니다. 경계는 위의 그림과 같이 선형으로 구할 수도 있고, 비선형으로 구할 수도 있습니다.


K-means Algorithm

Image

주어진 데이터들을 K 개의 클러스터(Cluster)로 나누는 알고리즘입니다. 데이터들의 거리를 기반으로 가까운 데이터들끼리 군집(Group)화 하면서 클러스터를 나눕니다. 간단히 사용하기에 괜찮은 알고리즘이긴 하지만 다음과 같은 한계점을 있습니다.

  • 파라메터 K 값에 따라 그 결과가 완전히 달라집니다.
  • 이상값(outlier)에 따라 중심값이 크게 왜곡될 수 있습니다.
  • 구형(spherical) 데이터가 아닌 경우 적절하지 않은 상황이 발생할 수 있습니다.

머신 러닝(Machine Learning)

|
'머신 러닝(Machine Learning)'은 사람이 컴퓨터에게 직접 명령을 내리는 것이 아니라 컴퓨터가 스스로 다수의 데이터를 이용하여 학습을 하고, 자동으로 문제를 풀어나가는 기술을 말합니다.

Machine Learning는 구글의 알파고 로 인해서 갑자기 더 유명해졌습니다.

Machine Learning에 대해서 구체적인 예를 들어보도록 하겠습니다.

메일에서 스팸(Spam) 메일을 구분하는 방법입니다.

  • 제일 쉬운 방법은 블랙 리스트를 관리하는 방법입니다.
  • 하지만 메일을 보낸 사람의 이름을 바꿔버리면 ?
  • 그 다음으로 쉬운 방법은 제목에서 특정 단어들을 찾는 방법입니다.
  • 하지만 메일 제목을 ‘광.고*‘와 같은 식으로 바꿔버리면 ?

이제부터는 단순히 룰(Rule)기반 알고리즘만으로는 스팸 메일을 구분하기 힘들어집니다. 하지만, Machine Learning을 적용해서 좀 더 효율적으로 처리할 수 있습니다.

먼저 컴퓨터에게 정상 메일과 스팸 메일들을 잔뜩 주고나서, 학습을 하도록 합니다. 그러면 컴퓨터가 스스로 메일들을 분류하고 학습해가며 스팸 메일을 찾아낼 수 있게 됩니다.
(물론 100% 정답을 찾아내는 것은 어렵습니다. 대신 데이터가 많으면 많을 수록, 학습을 많이 하면 할 수록 정답에 가까운 확률로 스팸 메일을 찾아낼 수 있게 될 것입니다.)

위의 예제와 같이 Machine Learning은 Rule 기반의 동작과는 거리가 좀 있습니다.

보통 Machine Learning과 데이터 마이닝(Data Mining)이 같은 개념으로 사용되기도 하는데, 사실은 조금 다른 특징이 있습니다.

  • Machine Learning는 대규모의 Training Data를 기반으로 학습을 하여 예측에 중점을 두고 있습니다.
  • Data Mining는 이름 그대로 Data를 발굴하는 것입니다. 미처 몰랐던 정보를 찾아내는 것에 중점을 두고 있습니다.

Machine Learning은 다양한 알고리즘들이 쓰이며, 최고의 알고리즘은 없습니다. 다만, 용도에 따라 더 효율적인 알고리즘은 존재할 수 있습니다.

Machine Learning를 간단한게 분류지어보면 다음과 같습니다.


Taxonomy of Machine Learning

Image

다음 번에는 Machine Learning에 대한 대표적인 알고리즘들에 대해서 간단히 포스팅 하도록 하겠습니다.

Shared Preference 사용하기

|

보통 데이터를 저장할 때는 데이터베이스를 이용하는 경우가 많은데, 간단한 데이터만 저장하기 위해서 데이터베이스를 사용하는 것은 불필요한 작업일 경우가 많습니다. 이런 경우는 Shared Preference를 이용하면 간단하게 구현할 수 있습니다.

Shared Preference는 key-value 방식으로 데이터를 저장합니다. 그래서 원하는 데이터들을 쉽게 저장하고 불러올 수 있습니다.

다음은 Shared Preference를 사용하는 예제 코드입니다.


SnowPreferenceManager.java

package com.lnc.cuppadata.gui.renewal.main;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;


public class SnowPreferenceManager {

  public static final int VIEWMODE_DAY = 0;
  public static final int VIEWMODE_WEEK = 1;
  public static final int VIEWMODE_MONTH = 2;

  private static final String PREFERENCE_VIEW_MODE = "pref_view_mode";
  private static final String PREFERENCE_USER_ID = "pref_user_id";

  public static void setViewMode(Context context, int viewMode) {
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
    SharedPreferences.Editor editor = prefs.edit();
    editor.putInt(PREFERENCE_VIEW_MODE, viewMode);
    editor.commit();
  }

  public static int getViewMode(Context context) {
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
    return prefs.getInt(PREFERENCE_VIEW_MODE, VIEWMODE_DAY);
  }

  public static void setUserId(Context context, String userId) {
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
    SharedPreferences.Editor editor = prefs.edit();
    editor.putString(PREFERENCE_USER_ID, userId);
    editor.commit();
  }

  public static String getUserId(Context context) {
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
    return prefs.getString(PREFERENCE_USER_ID, "");
  }
}

시간(Time) 관련 함수 코드

|

다음 함수들은 시간과 관련된 기능을 제공하는 함수들입니다. 그때그때 필요한 기능들을 일일이 찾아서 사용해도 되지만, 제 경우는 별도의 클래스로 만들어서 입맛에 맞게 사용하는 것이 더 편리하더군요. 그래서 저는 시간 관련 함수들을 TimeUtils Class로 만들어서 사용하고 있습니다.


현재 시간을 Long 값으로 리턴하는 코드

public static long getTimeAsLong() {
  Calendar calendar = Calendar.getInstance();
  return calendar.getTimeInMillis();
}


현재 시간을 String으로 리턴하는 코드

public static String getTimeAsString(String format) {
  Date date = new Date(getTimeAsLong());
  SimpleDateFormat sdf = new SimpleDateFormat(format,
      Locale.getDefault());

  return sdf.format(date);
}


입력받은 시간(String)을 Long으로 리턴하는 코드

public static Long getTimeAsLong(String format, String text) {
  try {
    SimpleDateFormat sdf = new SimpleDateFormat(format,
        Locale.getDefault());
    Date date = sdf.parse(text);
    return date.getTime();
  } catch(Exception e) {
    e.printStackTrace();
  }
  return new Long(-1);
}


입력받은 시간(Long)을 String으로 리턴하는 코드

public static String getTimeAsString(String format, long time) {
  Date date = new Date(time);
  SimpleDateFormat sdf = new SimpleDateFormat(format,
      Locale.getDefault());

  return sdf.format(date);
}


오늘 시작 시간을 Long으로 리턴하는 코드

public static Long getTodayFrom() {
  String date = SnowTimeUtil.getTimeAsString("yyyy-MM-dd");
  long time = SnowTimeUtil.getTimeAsLong("yyyy-MM-dd", date);

  Calendar calendar = Calendar.getInstance();
  calendar.setTimeInMillis(time);
  calendar.set(Calendar.HOUR, 0);
  calendar.set(Calendar.MINUTE, 0);
  calendar.set(Calendar.SECOND, 0);

  return calendar.getTimeInMillis();
}


오늘 종료 시간을 Long으로 리턴하는 코드

public static Long getTodayTo() {
  String date = SnowTimeUtil.getTimeAsString("yyyy-MM-dd");
  long time = SnowTimeUtil.getTimeAsLong("yyyy-MM-dd", date);

  Calendar calendar = Calendar.getInstance();
  calendar.setTimeInMillis(time);
  calendar.set(Calendar.HOUR, 0);
  calendar.set(Calendar.MINUTE, 0);
  calendar.set(Calendar.SECOND, 0);
  calendar.add(Calendar.DAY_OF_MONTH, 1);

  return calendar.getTimeInMillis() - 1;
}

Foreground Service 용 Notification 꾸미기

|

안드로이드에서 서비스는 크게 백그라운드 서비스(Background Service)와 포그라운드 서비스(Foreground Service)로 나눌 수 있습니다.


Background Service

우리가 흔히 말하는 서비스가 백그라운드 서비스 형태입니다. 시스템의 메모리가 부족할 경우 시스템이 해당 서비스를 강제로 종료시킬 수 있습니다. onStartCommand()의 파라메터를 이용하여, 서비스의 종료시 동작을 정의할 수 있습니다.

  • START_NOT_STICKY : 서비스가 종료되었을 때, 서비스 재 실행을 하지 않음
  • START_STICKY : 서비스가 종료되었을 때, 서비스를 재 실행 함. onStartCommand()를 호출하며 파라메터는 null 임
  • START_REDELIVER_INTENT : 서비스를 재 실행하며, 기존의 Intent 파라메터를 이용하여 onStartCommand()를 호출함


Foreground Service

서비스의 우선 순위가 높아서, 시스템의 메모리가 부족하더라도 강제로 종료시키지 않습니다. 대신 상태바에 Notification이 표시됩니다. 과거에는 상태바에 Notification을 표시하지 않고도 Foreground Service로 실행할 수가 있었는데, 현재는 구글 정책으로 Foreground Service를 수행하기 위해서는 무조건 사용자에게 표시를 해야 하도록 변경되었습니다.


Foreground Service 만들기

서비스는 기본적으로 Background Service 입니다. Foreground Service를 만들기 위해서는 서비스 내부에 다음과 같은 코드를 작성하면 됩니다.

startForeground(1, new Notification());

이렇게 하면, 알아서 Notification을 표시해주며 서비스는 Foreground 형태로 동작하게 됩니다.


Customize Notification 예제

이 때, Notification의 모습을 바꾸고 싶을 때는 어떻게 하면 될까요? 다음과 같은 코드를 이용해서 Notification의 모습을 바꿔줄 수 있습니다.


notification_foreground.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="horizontal">

  <ImageView
    android:layout_width="44dp"
    android:layout_height="44dp"
    android:layout_marginLeft="10dp"
    android:layout_gravity="center_vertical"
    android:src="@mipmap/ic_launcher" />

  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:orientation="vertical">

    <TextView
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_gravity="center_vertical"
      android:text="@string/notification_title"
      android:textColor="@color/black"
      android:textSize="18sp" />

    <TextView
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_gravity="center_vertical"
      android:text="@string/notification_message"
      android:textColor="@color/darkgray"
      android:textSize="14sp" />
  </LinearLayout>
</LinearLayout>


SampleService.java

RemoteViews remoteViews = new RemoteViews(getPackageName(),
    R.layout.notification_foreground);
Notification.Builder mBuilder = new Notification.Builder(this)
    .setSmallIcon(R.mipmap.ic_launcher).setContent(remoteViews);

startForeground(1, mBuilder.build());


여기에 Notification을 눌렀을 때 동작하는 PendingIntent를 연결하고 싶으면 다음과 같이 작성하면 됩니다.

Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
RemoteViews remoteViews = new RemoteViews(getPackageName(),
    R.layout.notification_foreground);
Notification.Builder mBuilder = new Notification.Builder(this)
    .setSmallIcon(R.mipmap.ic_launcher)
    .setContent(remoteViews)
    .setContentIntent(pendingIntent);
startForeground(1, mBuilder.build());