original source : https://youtu.be/_xNkVNaC9AI

기본 전제)

service는 server 이며 이에 연결되는 activity는 client라고 이해하면 이해한다.


MainActivity

package com.codingwithmitch.boundserviceexample1;

import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";


    // UI Components
    private ProgressBar mProgressBar;
    private TextView mTextView;
    private Button mButton;


    // Vars
    private MyService mService;
    private MainActivityViewModel mViewModel;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mProgressBar = findViewById(R.id.progresss_bar);
        mTextView = findViewById(R.id.text_view);
        mButton = findViewById(R.id.toggle_updates);

        mViewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
        setObservers();

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                toggleUpdates();
            }
        });
    }

    private void toggleUpdates(){
        if(mService != null){
            if(mService.getProgress() == mService.getMaxValue()){
                mService.resetTask();
                mButton.setText("Start");
            }
            else{
                if(mService.getIsPaused()){
                    mService.unPausePretendLongRunningTask();
                    mViewModel.setIsProgressBarUpdating(true);
                }
                else{
                    mService.pausePretendLongRunningTask();
                    mViewModel.setIsProgressBarUpdating(false);
                }
            }

        }
    }

    private void setObservers(){
        mViewModel.getBinder().observe(this, new Observer<MyService.MyBinder>() {
            @Override
            public void onChanged(@Nullable MyService.MyBinder myBinder) {
                if(myBinder == null){
                    Log.d(TAG, "onChanged: unbound from service");
                }
                else{
                    Log.d(TAG, "onChanged: bound to service.");
                    mService = myBinder.getService();
                }
            }
        });

        mViewModel.getIsProgressBarUpdating().observe(this, new Observer<Boolean>() {
            @Override
            public void onChanged(@Nullable final Boolean aBoolean) {
                final Handler handler = new Handler();
                final Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        if(mViewModel.getIsProgressBarUpdating.getValue()){
                            if(mViewModel.getBinder().getValue() != null){ // meaning the service is bound
                                if(mService.getProgress() == mService.getMaxValue()){
                                    mViewModel.setIsProgressBarUpdating(false);
                                }
                                mProgressBar.setProgress(mService.getProgress());
                                mProgressBar.setMax(mService.getMaxValue());
                                String progress =
                                        String.valueOf(100 * mService.getProgress() / mService.getMaxValue()) + "%";
                                mTextView.setText(progress);
                            }
                            handler.postDelayed(this, 100);
                        }
                        else{
                            handler.removeCallbacks(this);
                        }
                    }
                };

                // control what the button shows
                if(aBoolean){
                    mButton.setText("Pause");
                    handler.postDelayed(runnable, 100);

                }
                else{
                    if(mService.getProgress() == mService.getMaxValue()){
                        mButton.setText("Restart");
                    }
                    else{
                        mButton.setText("Start");
                    }
                }
            }
        });
    }


    @Override
    protected void onResume() {
        super.onResume();
        startService();
    }


    @Override
    protected void onStop() {
        super.onStop();
        if(mViewModel.getBinder() != null){
            unbindService(mViewModel.getServiceConnection());
        }
    }

    private void startService(){
        Intent serviceIntent = new Intent(this, MyService.class);
        startService(serviceIntent);

        bindService();
    }

    private void bindService(){
        Intent serviceBindIntent =  new Intent(this, MyService.class);
        bindService(serviceBindIntent, mViewModel.getServiceConnection(), Context.BIND_AUTO_CREATE);
    }


}

위 코드에서 runnable 호출을 runnable 자체내에서 수행함으로써 looping를 한 부분이 생소했다.아래 그림 참조

또한 bindService()도 처음에는 이해하기 힘들었다. 두번째 파라미터 service connection을 전달부분이 어려웠다. 아래 MainActivityViewModel 코드를 확인하면 ServiceConnection obj 생성시에는 특별한 파라미터를 필요로 하지 않는다. ServiceConnection 는 activity에 service가 연결될때 호출되는 callback obj 로 추측한다. 

MainActivityViewModel

package com.codingwithmitch.boundserviceexample1;

import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

public class MainActivityViewModel extends ViewModel {

    private static final String TAG = "MainActivityViewModel";

    private MutableLiveData<Boolean> mIsProgressBarUpdating = new MutableLiveData<>();
    private MutableLiveData<MyService.MyBinder> mBinder = new MutableLiveData<>();


    // Keeping this in here because it doesn't require a context
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder iBinder) {
            Log.d(TAG, "ServiceConnection: connected to service.");
            // We've bound to MyService, cast the IBinder and get MyBinder instance
            MyService.MyBinder binder = (MyService.MyBinder) iBinder;
            mBinder.postValue(binder);
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            Log.d(TAG, "ServiceConnection: disconnected from service.");
            mBinder.postValue(null);
        }
    };


    public ServiceConnection getServiceConnection(){
        return serviceConnection;
    }

    public LiveData<MyService.MyBinder> getBinder(){
        return mBinder;
    }


    public LiveData<Boolean> getIsProgressBarUpdating(){
        return mIsProgressBarUpdating;
    }

    public void setIsProgressBarUpdating(boolean isUpdating){
        mIsProgressBarUpdating.postValue(isUpdating);
    }


}


MyService

package com.codingwithmitch.boundserviceexample1;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

public class MyService extends Service {

    private static final String TAG = "MyService";

    private final IBinder mBinder = new MyBinder();
    private Handler mHandler;
    private int mProgress, mMaxValue;
    private Boolean mIsPaused;

    @Override
    public void onCreate() {
        super.onCreate();
        mHandler = new Handler();
        mProgress = 0;
        mIsPaused = true;
        mMaxValue = 5000;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }


    public class MyBinder extends Binder{

        MyService getService(){
            return MyService.this;
        }

    }

    public Boolean getIsPaused(){
        return mIsPaused;
    }

    public int getProgress(){
        return mProgress;
    }

    public int getMaxValue(){
        return mMaxValue;
    }

    public void pausePretendLongRunningTask(){
        mIsPaused = true;
    }

    public void unPausePretendLongRunningTask(){
        mIsPaused = false;
        startPretendLongRunningTask();
    }

    public void startPretendLongRunningTask(){
        final Runnable runnable = new Runnable() {
            @Override
            public void run() {
                if(mProgress >= mMaxValue || mIsPaused){
                    Log.d(TAG, "run: removing callbacks");
                    mHandler.removeCallbacks(this); // remove callbacks from runnable
                    pausePretendLongRunningTask();
                }
                else{
                    Log.d(TAG, "run: progress: " + mProgress);
                    mProgress += 100; // increment the progress
                    mHandler.postDelayed(this, 100); // continue incrementing
                }
            }
        };
        mHandler.postDelayed(runnable, 100);
    }

    public void resetTask(){
        mProgress = 0;
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        Log.d(TAG, "onTaskRemoved: called.");
        stopSelf();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: called.");
    }
}

https://youtu.be/VNEdufXVMaU6 1 What is Text Classification
https://youtu.be/kxImnFg4ZiQ

image
image
image
image

6 2 Naive Bayes
https://youtu.be/j39c7Gjx2gE

image
image

감마 기호는 function을 의미한다.

6 3 Formalizing the Naive Bayes Classifier

https://youtu.be/VNEdufXVMaU

image
image
image

d 는 document, c는 class를 의미한다.

P(d|c) 는 likelihood probability, P© prior probability 

image

documents는 x features (단어들)의 연속으로 구성되어있다.

image

P(x1, x2, x3 …….  xn | c)의 경우 conditional 에 conditional에 중첩된 conditional probability 계산이 되는데 이런경우 복잡해지게 된다.

image

계산이 복잡해지는데 이를 간소화 시켜서 계산하는 방법을 사용한다. bag of words를 이용하거나 independent probability를 이용(모든 단어는 서로 independent하다고 가정)한다.

image
image

6 4 Naive Bayes Learning
https://youtu.be/3jR8TZG8T88

image

P(cj)는 전체 문서에서 특정 class의 문서가 나올 확률이다.

P(wi | cj)는 특정 class에서 특정 word가 나올 확률이다. 

image

위의 내용을 간단히 정리하면 특정 class의 모든 문서(시그마부분에 해당)를 하나로 만들고 그 안에서 특정단어들이 나오는 횟수를 이용해 P(wi | cj)를 구한다. 

image

training documents에 fantastic이라는 단어가 한번도 사용되지 않았다. 그런데 새로 주어진 문장에서는 이 단어가 사용되었다고 한다면 이전에는 한번도 나온적이 없었으므로 확률이 0이 되버리게 된다. 즉 새로운 단어에 대한 고려가 전혀 없는 것이다. 이 문제의 해결방법은 아래 그림 참조

image

모든 단어의 출현횟수를 1씩 늘려줌으로써 최소횟수가 0이 아닌 1이 되게된다.

image

우측 하단은 add alpha가 적용된 공식이다.

image

training doc에서 한번도 출현하지 않은 단어는 unknown word로 위와 같이 처리한다.

6 5 Naive Bayes Relationship to Language Modeling
https://youtu.be/LRFdF9J__Tc

image
image
image

6 6 Multinomial Naive Bayes A Worked Example
https://youtu.be/OWGVQfuvNMk

image

P©는 전체 문서들중에 class c 문서가 나올 확률이다. 여기서 사용된 P(w | c)는 add one smoothing이 적용된 특정 class에서 특정 단어들이 나올 확률이다. 

위의 예제는 주어진 training data를 이용해 만든 모델로 test doc의 class를 예상하는 과정이다. 오른쪽 하단의 내용은 주어진 문서 d5가 어떤 클래스인지를 비교 추측하는 것이다. 각 c , j class일 확률을 비교 한다. 이때 d5는 공통으로 들어가는 부분이므로 생략했다.  

image

6 7 Precision, Recall, and the F measure
https://youtu.be/81j2nzzBHUw

참조자료)

andrew ng https://youtu.be/wGw6R8AbcuI

https://youtu.be/W5meQnGACGo

image

위그림이 좀더 이해하기 쉽다.

image

accuracy 측정 방법

image
image

6 8 Text Classification Evaluation
https://youtu.be/TdkWIxGoiak

image
image
image
image
image
image

6 9 Practical Issues in Text Classification
https://youtu.be/uIvSHmsLs-U

text classification에 대한 전반적인 내용을 다루고 있다.

training data가 없는 경우 개발자가 직접 손수 rule을 만든다.

knn k nearest neighbors를 말한다.

보통 training data의 양이 많은 경우 classifier간의 성능차는 별로 없어지게 된다. 

자동, 수동 섞어서 처리한다. 

확률의 곱이 많이 이어지는 경우 그 숫자가 매우 작아지는 경우가 생기는데 이를 underflow라고 한다. 이를 방지 하기 위해 log값을 이용한다. log에서는 두 값이 곱이 합으로 계산되기 때문이다. 

upweighting : 단어의 위치나 사용방법에 따라 가중치를 두는 방법이다. 

5 1 The Spelling Correction Task
https://youtu.be/iYFqYL9RsRs

image
image
image

5 2 The Noisy Channel Model of Spelling
https://youtu.be/q6BFerSaUEA

image

원래의 단어가 noisy channel를 거쳐 noisy word가 될수 있다고 가정하고 noisy word (spelling이 맞지 않는 단어)를 가지고 원래 어떤 단어가 되어야 하는지 추측하는 과정을 보여주고 있다. 

image

x 는 misspelled된 잘못된 단어이다. 잘못된 단어 x가 주어진 상태에서 원래 단어 w일 확률을 구한다. 이를 beyes rule을 이용 변형하면 두번째 공식이 되고 여기서 P(x)는 최대값을 구하는 과정에서 중요하지 않으므로 생략할수 있다. 

image

결과적으로 나온 맨 밑줄의 공식에서 P(x|w)를 channel model, error model이라고 한다. P(w)를 language model이라고 한다.

image
image

기존 edit distance는 insertion, deletion, substitution 만을 이야기 했다. Damerau-Levenshtein은 여기에 transposition을 추가 했다.

image

“acress” 의 edit distance 1의 예

image

 80%의 spelling 문제는 edit distance 1 안에서 발생. 대부분은 edit distance 2안에서 발생한다.

image

이전 강의에서 배운 기존의 language model을 사용한다.

image

unigram을 이용한 language model을 보여주고 있다.

image
image
image
image

confusion matrix의 예를 볼수 있는 곳

image

이부분은 잘 이해하지 못했다. (원래 논문 https://www.aclweb.org/anthology/C90-2036)

image

confusion matrix를 이용 channel modle의 확률을 구하고 language model을 통해 구해진 확률을 곱하면 최종의 확률을 얻을수 있다. 

image

한 단어(이 강의에서는 unigram을 language model로 사용)의 철자들을 이용해 얻은 확률보다 때로는 bigram을 이용해 보완하면 보다 좋을 결과를 얻을수 있는 것을 보여주고 있다.

image

자신이 만든 모델을 평가하고 싶을때 참조사용할만한 웹페이지를 보여주고 있다.

5 3 Real Word Spelling Correction
https://youtu.be/bZNU2FwTXDc

image

실제 존재하는 단어로 misspelled되는 경우가 전체 misspelled 문제의 25 – 40%를 차지한다. 예를 들어 minutes를 사용해야하는데 minuets를 사용하는 경우

image
image

단어 하나 하나 마다의 틀린 스펠 단어의 후보를 여러개 뽑아 놓는 것을 보여준다. 

image

각 단어마다의 여러 후보를 뽑고 이의 총 조합 경우를 확인한다. 

image

각 단어별로 여러개의 단어후보를 뽑아 모든 조합의 경우를 확인하는 것이 번거로울수 있다. 보통은 한 문장에서 한단어가 문제가 있는 경우가 많기 때문이다. 

image

non-word spelling problem에서 사용했던 channel model방법을 그대로 사용한다 다만 real-word spelling problem의 경우 맞는 단어가 주어진 상황에서 맞는 단어일 확률을 추가해서 확인해준다.

image

5 4 State of the Art Systems
https://youtu.be/aAvOOZRsby8

spelling correction 결과의 신뢰도에 따른 실제 어플리케이션에서 사용자에게 제시하는 방법들

이론상으로 channel model (error model) 과 language model (prior model) 의 곱으로 spelling의 맞을 확률을 구한다고는 했으나 사실 순수하게 두 값의 곱으로 결과값으로 사용하지는 않고 위의 그림처럼 람다값을 이용해 준다. 이 람다값은 test set을 이용해 통해 얻는다.

위의 3개의 그림처럼 다양한 기법을 총체적으로 사용한다.

classifier 에 따라 어떤 단어를 선택할지를 정할수 있다.