아래는 배틀필드4의 멀티플레이 티져 영상입니다.

 

 

현재 예약 판매중인 배틀필드(Battle Field) 4의 프리미엄(Premium)이 공개되었습니다.

 

 저는 이미 디지털 디럭스 에디션(Digital Deluxe Edition)을 예약구매 한 상태였기 때문에 혹여나 오리진(Origin)에게 '당한' 것인가 걱정이 되어 인터넷을 찾아보았습니다.

 

☞ 오리진 배틀필드4 구매페이지 링크: http://www.battlefield.com/kr/battlefield-4/buy

 

 

찾아본 결과 디지털 디럭스 에디션과 프리미엄은 별개 구입해야 하는 것이라고 합니다.

 

다음은 EA의 고객센터에 올라온 질문과 이에 대한 회사측의 답변입니다. 이미지 하단에 번역을 해 놓았습니다.

 

질문:

배틀필드 4™ 디지털 디럭스와 Battlefiel​d 4™ 프리미엄중에 뭘 사야되나요?

‎2013년 08월 23일 오전02:34

 

안녕하세요

 

질문이 있습니다

 

배틀필드 4™ 디지털 디럭스와 Battlefiel​d 4™ 프리미엄중에 뭘 사야되나요?

 

둘 다 사야되는건가요?

 

답변:

답변: 배틀필드 4™ 디지털 디럭스와 Battlefiel​d 4™ 프리미엄중에 뭘 사야되나요?

2013년 08월 23일 오후11:42

 

프리미엄은 기본 게임을 포함하고 있지 않으며 기본 게임은 스탠다드나 디지털 디럭스 에디션으로 따로 구매하셔야 합니다.

프리미엄 멤버쉽을 통해 다음과 같은 새로운 게임 혜택을 경험하실 수 있습니다:

 

+ 5가지 디지털 확장팩

+ 5가지 확장팩 2주 먼저 이용
+ 독점 개인화 옵션 
+ 12가지 보너스 [배틀필드 4] 배틀팩
+ XBOX 360에서 XBOX ONE으로, PS3에서 PS4로 프리미엄 멤버쉽 완벽 전송 
+ 그 외에도 다양한 혜택이 있습니다!

 

 

결국 정리하자면 이번에 추가된 프리미엄은 일종의 '멤버쉽'을 판매하는 것으로써, 기본 게임이 포함되어있지 않습니다.

 

따라서 프리미엄만을 구입한 경우에는 게임을 이용할 수 없습니다.

 

지금 판매중인 가격은 아래와 같습니다.

 

  - 디지털 디럭스: 49,500원(할인가격, 원가는 55,000원)

  - 스탠다드       : 40,500원(할인가격, 원가는 45,000원)

  - 프리미엄       : 55,000원

 

 

 

만약 [디지털 디럭스 + 프리미엄]을 구매하게 되면 11만원이 필요한 것입니다.

 

디지털 디럭스나 스탠다드는 예약구매 시 DLC중 하나인 [차이나 라이징(China Rising)]을 제공해준다고 하네요.

 

물론 디지털 디럭스가 스탠다드보다 혜택은 '쬐끔' 더 많습니다. (베타 독점 접속권이라던가 골드팩인지 뭔지 하는 등)

 

그냥 조금 더 일찍 플레이해보고 싶다면 디지털 디럭스를 사시면 됩니다.

 

자세한 혜택은 본문 상단에 링크된 구매페이지에서 확인하실 수 있습니다.

 

 

 

 

저는 일단 예약구매한 디지털 디럭스를 이용하고 프리미엄은 추후 사람들 반응을 보고 구매 여부를 결정할 생각입니다.

 

어쩌면 소위 말하는 오리진의 '호갱'이 될 지도 모르겠네요.

Posted by Kugi
,



알고리즘 영역에서 분할정복법(Divide-and-Conquer)은 말 그대로 주어진 문제를 분할하여 해결하는 방법을 말한다. 즉, 한 번에 해결하기 어려운 문제를 작은 단위의 부문제들(subproblems)로 쪼개어 해결하는 방법이다. 이름에 하필 '정복(Conquer)'이라는 단어가 들어가는 이유는 문제 해결 방식이 바로 그 유명한 '나폴레옹 황제'가 활용했던 분할정복(Divide and Conquer, 또는 Divide and Rule) 전략과 흡사했기 때문이다. 직접 상대하기 버거운 많은 수의 적군을 조금씩 쪼개어 각개격파한다는 이해하기 매우 쉬운 전술이다.

 

분할정복법은 대개 재귀적으로 구현되기 때문에 마찬가지로 문제를 최소단위까지 쪼개어 해결하는 일반적인 재귀적 풀이법과 어떻게 다른지 감이 안올 수도 있다. 그런 경우를 위해서 일반적인 재귀적 해법과 다른 분할정복법의 한 가지 특징을 꼽자면 다음 그림에서처럼 분할정복법에서는 문제를 한 조각과 나머지부분으로 나누는 것이 아니라, 거의 균등한 크기의 부분 문제로 나눈다는 점이다.

 

 

분할정복 알고리즘에는 보통 다음과 같은 세 가지 과정이 있다.

 

1. 문제를 더 작은 문제(부문제)로 분할 (Divide)

 

2. 각 부문제에서 구한 답을 분할하기 전의 원래 문제에 대한 답이 되도록 합병하는 과정 (Merge)

 

3. 더 이상 쪼갤 필요가 없거나 쪼갤 수 없는 문제에 대해 답을 구하는 과정 (Base case)

 

 

어떤 문제에 분할정복법을 적용할 필요성이 있는지 알아보려면 다음의 두 가지 조건을 검사해보면 된다.

 

1. 문제를 여러 부문제들로 쪼개는 것이 가능한가? (Divide)

 

2. 부문제들의 답을 조합하여 본래 문제의 답을 효율적으로 구할 수 있는가? (Merge & Conquer)

 

 

위 조건 중 2번에서 '효율적'이라는 말을 강조한 이유는 분할정복법을 적용시킬 수 있다고 한들 무식하게 푸는 방법보다 비효율적이면 이 방법을 사용하는 의미가 없기 때문이다. 그 말은 즉, 분할정복법의 장점은 처리 속도라는 것이다. 간단한 예제를 통해서 정말로 처리 속도에 이득이 있는지를 확인해보자. 분할정복법은 퀵 정렬(Quick Sorting), 합병정렬(Merge Sorting), 이진검색(Binary Search), 고속퓨리에변환(FFT), 유클리드 호제법(Euclidean algorithm) 등이 있으나 여기서는 쉽게 이해할 수 있도록 이진검색(Binary Search) 알고리즘을 예로 들겠다. 이진검색(또는 이분검색)이 뭔지 모르는 사람은 우리가 사전에서 단어를 찾을 때 어떤 순서로 페이지를 넘기는지를 떠올려보면 이해하기 쉬울 것이다.

 

아무튼, 본론으로 넘어가서

우리가 알파벳 'A'로 시작하는 단어가 적힌 n장의 카드 중에서 'Apple'이라는 단어가 몇 번째 카드에 적혀있는지를 찾는다고 하자.

 

각각의 카드에는 중복된 단어가 존재하지 않으며 카드의 순서는 알파벳 사전순서로 되어 있다. 작업의 단위는 '카드를 한 장 확인하는 행위'이다.

 

 

이 경우 카드의 첫 장부터 끝 장까지를 모두 확인한다면 운이 좋을 때는 한 번만에 찾겠지만 운이 나쁘면 n번의 확인 작업을 필요로 한다. 'Apple'을 제외한 나머지 영단어들이 카드에 써 있을 확률이 모두 동등하다면 결국 이 방법을 썼을 때 평균적으로 번의 확인 작업을 해야만 한다. 그렇다면 분할정복법인 이진검색을 사용하면 어떨까? 카드에 써진 알파벳은 사전 순서로 나열되어있기 때문에 전체 카드 중에서 중간에 있는 번째 카드를 확인하고, 그 카드가 찾는 단어인 'Apple'보다 더 빠른 단어이면 번째 카드 다음으로 나열된 카드들만을 확인하면 되고 반대로 'Apple'보다 더 나중에 나와야 할 단어였다면 번째 카드 앞에 나열된 카드들만을 확인하면 될 것이다. 따라서 확인해야 할 카드의 수는 절반 이하로 줄었고, 다시 이 카드들에 대해서 방금 적용한 것처럼 중간에 있는 카드를 확인하는 절차를 반복하면 된다. 한 번 확인할 때마다 남은 카드의 수가 최대 절반 이하로 줄기 때문에 이 방법을 적용하여 문제를 해결하는 경우 우리가 해야만 하는 작업의 수는 아무리 많아봤자 번에 불과하다. 카드의 수가 64장만 되어도 위 두 방법에서 수행되는 작업은 각각 최대 64회, 6회로 큰 차이를 보인다. 따라서 분할정복법을 적용할만한 문제라면 그냥 해결하는 것보다 분할정복법을 적용하는 편이 훨씬 이득이라는 것을 알 수 있다.

 

 

이진검색에서는 분할하는 것만으로 문제가 해결된 것 처럼 보인다. 그러나 병합과정은 나눠진 카드에 각각 순서번호가 부여되어있고 이것을 몇번인지 바로 알아낼 수 있는 시점에서 이미 완료된 것이다. 좀 더 복잡한 알고리즘에서는 답을 구하기 위한 합병 과정을 쉽게 눈치챌 수 있을 것이다. 합병정렬(또는 병합정렬), 쉬트라쎈(Strassan)의 행렬곱셈 알고리즘, 카라츠바 빠른곱셈 알고리즘 등을 참고해보면 좋다.

Posted by Kugi
,



개발보조 명목으로 GIGABYTE U2442F 노트북(울트라북)을 구입하였다.

 

 

 

OS 미탑재인줄 알고 샀는데 받고 보니 Windows 8 탑재 기종이었다.

 

Windows 8은 도저히 적응을 못할 것 같아서 하드디스크를 시스템 영역까지 전부 밀어버리고 Mint 13(리눅스-우분투계열)과 Windows 7을 설치하기로 했다.

 

탑재된 바이오스에서는 부팅하면 다음과 같은 세 가지 메뉴가 뜬다.

Press <F2>  for Enter SETUP.

Press <F12> for BBS POPUP Menu.

Press <F9>  for System Recovery.

 

구매할 때 ODD 옵션을 적용하지 않아서 CD를 돌릴 수 없기 때문에 USB로 OS 설치를 해야만 하였다.

 

그런데 USB로 부팅을 하기 위해서 <F12>를 누르자 문제가 생겼다.

 

바이오스가 재부팅을 무한히 반복하게 된 것이다.

 

심지어 부트 화면이 보이기도 전에 시스템에 재부팅되어 전원만 들어왔다가 꺼졌다를 반복했다.

 

아무래도 바이오스 메모리를 날리는 것 밖에는 답이 없을 것 같았다.

 

메모리를 날리려면 바이오스의 수은전지를 제거하여 방전시킨 후 다시 꽂아야만 하는데 Desktop PC라면 간단했겠지만

 

좁은 공간에 모든 하드웨어 장치를 집어넣은 울트라북이기 때문에 고생하지 않을 수 없었다.

 

바이오스 배터리를 제거하기 위해 노트북을 분해하면서 그 과정을 다음과 같이 몇 장의 사진에 담아보았다.

 

 

Posted by Kugi
,



* 이 문서는 일본어 위키백과의 単純ベイズ分類器」를 번역한 것으로써, 오류가 있을 수 있습니다.

 

* 번역한 원본 문서는 한국날짜로 2013년 7월 22일 수집되었습니다.

   単純ベイズ分類器 - Wikipedia.mht

 

*'単純ベイズ分類器(단순베이즈분류기)'는 '나이브 베이즈 분류기'로 번역하였습니다.

 

* 위키백과 연결링크는 페이지가 존재하는 경우 한국어 또는 영어 페이지로 연결했습니다.

 

* 이해를 쉽게 하도록 하기 위하여 '단어를 이용한 신문기사의 종류 분류'를 예로 들자면

 

  본문에 나오는 '클래스(C)'는 분류할 부류를 뜻하므로 c의 종류는 '정치', '사회', '경제', 'IT/과학' 등이고

 

  본문에 나오는 '특징변수(F)'는 분류할 대상을 뜻하므로 문서 내에 포함된 단어의 수가 n개라면 f의 범위는 f1~fn입니다.

 

* 본문에서 말하는 평활화 기법인 Add-1 smoothing은 Laplace Smoothing 입니다.

 

 


 

 

나이브 베이즈 분류기

 

나이브 베이즈 분류기(영어: Naive Bayes classifier)는 단순한 확률적 분류기이다.

 

 

개요


 

나이브 베이즈 분류기의 기반 확률 모델은 강한(간단한) 독립가정을 적용한 베이즈 정리에 기반하고 있으며, 좀 더 정확하게 말하자면 「독립특징모델; independent feature model」라고 불러야 할 것이다.

 

확률모델의 성질로 인해 나이브 베이즈 분류기는 지도학습(supervised learning) 환경에서 효율적으로 훈련할 수 있다. 많은 실제 사례에서 나이브 베이즈 분류기의 파라미터를 추정하기 위해 최대우도추정법(最尤法; maximum likelihood estimation)을 사용한다. 즉, 나이브 베이즈 분류기를 사용하기 위해서 베이즈확률이나 그 외의 베이즈 기법을 사용할 필요는 없다.

 

구상[設計; design] 가정이 매우 단순함에도 불구하고 나이브 베이즈 분류기는 복잡한 실제 상황에서 예상보다 훨씬 잘 작동한다. 최근, 베이즈 분류 문제의 주의깊은 분석을 통해 나이브 베이즈 분류기의 효율성에 이론적인 이유가 있다는 것이 밝혀졌다. 나이브 베이지안 분류기의 장점은 분류에 필수적인 파라미터(변수군의 평군과 분산)을 추정하는 데 필요한 훈련 데이터 양이 적다는 점이다. 변수군은 독립적이라고 가정됐기 때문에 각 클래스에 대한 변수의 분산만이 필요하며, 공분산행렬 전체는 불필요하다.

 

 

나이브 베이즈 확률 모델


 

추상적으로 분류기의 확률모델은 다음과 같은 종속 클래스 변수 에 대한 조건부 모델이다. 클래스는 몇가지 특징변수 부터 에 의존한다.

 

   

 

문제는 특징 수 이 큰 경우, 또는 특징이 취할 수 있는 값의 범위가 큰 경우에 확률표를 토대로 한 것과 같은 모델들은 현실적이지 않다는 점이다. 따라서 모델을 좀 더 다루기 쉽게 변형시킨다.

 

베이즈 정리를 써서 다음과 같이 된다.

 

   

 

이 식은 영어로 나타내면 다음과 같다. (Posterior=사후, Prior=사전, Likelihood=공산, Evidence=결과)

 

   

 

실질적으로 분모는 에 의존하지 않는 일정한 를 가지므로 분자만을 고려하면 된다. 분자는 다음과 같이 표현되는 결합확률 모델과 같다.

 

   

 

여기에 조건부 확률의 정의를 반복해서 적용하면 다음과 같이 쓸 수 있다.

 

   

 

여기서 「나이브(단순)」한 조건부 독립성의 가정이 등장한다. 각 특징변수 이 조건부로 또 다른 특징변수 와 독립이다. 즉, 다음이 성립된다.

 

   

 

그러면 동시모델은 다음과 같이 나타낼 수 있다.

 

   

 

즉, 위에서 서술한 독립성의 가정에서 클래스 변수 의 조건분포는 다음과 같이 나타낸다.

 

   

 

여기서 에만 의존하는 계수이며, 특징변수들의 값을 이미 알고 있으면 상수가 된다.

 

이와 같은 모델은 이른바 「클래스 사전 확률」 과 독립확률분포 로 나뉘어 있기 때문에 다루기가 쉽다. 개의 클래스가 있고  모델을 개의 파라미터로 표현할 때 대응하는 나이브 베이즈 모델은 개의 파라미터를 가진다. 2항분류에서는 이며 은 예측에 사용되는 2값의 특징의 개수이다.

 

 

매개 변수 추정


 

모든 모델 파라미터(즉, 클래스 사전확률과 특징확률 분포)는 훈련된 집합에서 상대도수에 따라 추정할 수 있다. 이는 각각 확률 최대우도추정량이다. 이산적이 않은 특징의 경우 사전에 이산화(離散化; discretization)를 할 필요가 있다. 이산화에는 자율기법(즉흥적인 기법; unsupervised)과 교사기법(훈련 데이터에 근거한 기법; supervised)이 있다.

 

어떤 클래스와 특징값의 조합이 훈련에서는 나타나지 않는 경우, 도수에 근거한 확률추정은 0이 된다. 이것을 곱셈에 이용하면 곱에 0이 된다는 문제가 생긴다. 이를 막기 위해 확률 값의 추정을 약간 수정하여 어떤 조합의 확률값도 0이 되지 않도록 하는 방법이 널리 쓰인다. (Pseudo count; 의사 수)

 

 

확률 모델의 분류기 구축


 

지금까지의 설명으로부터 독립특징모델, 즉 나이브 베이지안 확률 모델이 도출되었다. 나이브 베이즈 분류기는 그 모델에 결정규칙을 합친 것이다. 흔히 쓰이는 결정규칙은 가장 그럴듯한 가설을 채용하는 방법으로, 최대사후확률(MAP) 결정규칙이라고 한다. 이러한 분류기를 함수 classify라고 하면 다음과 같이 나타낼 수 있다.

 

   

 


* 이후의 내용은 번역하지 않았습니다.

* 원문에서 이어지는 목차는 '논의', '예: 문서 분류', 'Complement Naive Bayes', '각주', '참고문헌', '관련 항목', '외부 링크', '소프트웨어' 입니다.

Posted by Kugi
,



* 이 문서는 번역에 오류가 있을 수 있습니다. 원본주소는 다음과 같습니다:

http://ebiquity.umbc.edu/blogger/2010/12/07/naive-bayes-classifier-in-50-lines/

 

* 더 나은 번역 의견이 있으시면 댓글로 달아주시기 바랍니다.

 

 

 

나이브 베이즈 분류기 in 50 lines

Krishnamurthy Viswanathan, 12:39am 7 December 2010

 

나이브 베이즈 분류기는 내가 대학원생으로서 얄팍한 경험을 하는 동안 주위에서 본 가장 범용성이 높은 기계 학습 알고리즘 중 하나이다. 그리고 재미삼아 구현을 해 보고 싶었다. 핵심은 구현이 계산의 형태로 축소되고, 테스트 부분을 포함한 전체 파이썬 모듈이 겨우 50여 라인의 코드로 끝났다는 것이다.  나는 실제로 성능을 평가해보지 않았기 때문에 모든 코멘트를 환영한다. 나는 파이썬 아마추어이고 분명 경험 많은 파이썬 해커라면 이 코드의 몇몇 어설픈 부분을 다듬을 수 있을 것이다.

 

 

직관과 디자인

여기서 분류기 상관관계의 정의 a는 다음과 같다. (위키피디아 발췌):

 

 

이것은 각각의 가능한 클래스 라벨에 대하여, 각 특징의 조건부 확률을 모두 곱하라는 의미이다. 이 말은 즉 우리가 이 분류기를 구현하기 위해서는 이 개별적인 조건부 확률들을 각각의 라벨, 특징마다 구하고 그것들을 라벨 의 사전확률과 함께 전부 곱해야 한다는 것을 의미한다. 여기서 얻어진 가장 큰 값의 라벨이 분류기로부터 반환되는 라벨이다.

 

이 개별적인 조건부 확률들을 계산하기 위해서 최대우도추정법을 사용한다. 입력/훈련 벡터들의 카운트를 사용해서 매우 짧은 문장으로 이 확률들을 근사한다.

 

즉 훈련 데이터로부터 라벨 가 발생하는 전체 횟수와 특징 와 라벨 가 동시에 발생하는 횟수의 비율을 구한다.

 

 

제로 확률 문제

만약 어떤 특징 와 어떤 라벨 가 훈련 데이터집합 내에서 동시에 일어나지 않게 되면 어떨까? 이러한 일이 테스트 데이터 내에서 발생할 때마다

는 0이 될 것이고 따라서 전체 곱도 0이 될 것이다. 이는 최대우도추정과 관련된 문제이다. 이 문제가 특정 훈련 도중에 관찰되지 않았다고 해서 테스트 데이터 내에서 발생하지 않는다는 의미는 아니다. 이 문제를 해결하기 위해서 평활화(smoothing)라고 알려진 방법을 사용한다. 우리는"add one 평활화"이라고 하는 가장 간단한 평활화를 코드에 사용한다

 

. 근본적으로 관측되지 않는 사건의 확률은 1보다 커야 한다. 우리는 각각의 0 카운트에 1을 더함으로써 이를 만족시킨다. 그물 효과에서는 어떤 확률질량을 non-zero 카운트 관측에서부터 zero 카운트 관측까지에 재분배해야만 한다. 따라서 전체 확률질량을 1로 유지하기 위하여 발생가능한 관측들의 개수만큼 각 라벨의 전체 카운트를 증가시켜야만 한다.

 

예를 들어, 두 클래스 이 있을 때, 평활화 된 최대우도추정 확률들은 다음과 같이 쓸 수 있다.

 

 

 

코드

간단하게 하기 위해 우리는 Weka의 ARFF 파일 포맷을 입력으로 사용할 것이다. 카운트와 특징 벡터의 디테일을 저장하기 위하여 몇 개의 사전들과 리스트들로 구성된 'Model'이라는 하나의 클래스를 사용한다. 이 구현에서는 오직 이산 값의 특징들만을 다룬다.

 

 

from __future__ import division
import collections
import math
 
class Model: 
        def __init__(self, arffFile):
                self.trainingFile = arffFile
                self.features = {}      #all feature names and their possible values (including the class label)
                self.featureNameList = []       #this is to maintain the order of features as in the arff
                self.featureCounts = collections.defaultdict(lambda: 1)
                                                #contains tuples of the form (label, feature_name, feature_value)
                self.featureVectors = []        #contains all the values and the label as the last entry
                self.labelCounts = collections.defaultdict(lambda: 0)   #these will be smoothed later

 

'features' 사전은 모든 발생가능한 특징들의 값을 저장한다. 'featureNameList'는 단순히 ARFF 파일에서 나타나는 같은 순서로 된 특징들의 이름들을 포함하는 리스트이다. 이는 특징들의 사전이 원래부터 순서를 갖지 않기 때문이며, 우리는 명시적으로 특징 순서를 유지할 필요가 있다. 'featureCounts'는 각 라벨 값과 특징 값이 동시에 발생한 실제 카운트를 포함한다. 이 사전의 키는 다음과 같은 형태의 튜플이다: (class_label, feature_name, feature_value). 따라서 'yes'라는 라벨의 특징 F1의 값이 1인 경우가 15번 관찰됬다면 사전 안에 다음과 같은 엔트리가 존재하는 것이다: {('yes', 'F1', '15)}. 이 사전 내의 카운트의 디폴트 값이 어떻게 '0'이 아닌 '1'이 됐는지에 주목하라. 이는 카운트가 평활화되었기 때문이다. 'featureVectors' 리스트는 사실 ARFF 파일의 모든 인풋 특징 벡터들을 포함한다. 이 벡터의 마지막 특징은 Weka ARFF 파일의 관례로써, 클래스 라벨 자기자신이다. 마지막으로 'labelCounts'는 클래스 라벨들 스스로의 카운트들, 즉 훈련하는 동안 라벨 가 몇 번 나왔는지를 저장한다.

 

또한 Model 클래스에는 다음과 같은 멤버 메서드가 있다.

 

 

def GetValues(self):
                file = open(self.trainingFile, 'r')
                for line in file:
                        if line[0] != '@':  #start of actual data
                                self.featureVectors.append(line.strip().lower().split(','))
                        else:   #feature definitions
                                if line.strip().lower().find('@data') == -1 and (not line.lower().startswith('@relation')):
                                        self.featureNameList.append(line.strip().split()[1])
                                        self.features[self.featureNameList[len(self.featureNameList) - 1]] =
                                                line[line.find('{')+1: line.find('}')].strip().split(',')
                file.close()

 

위의 메서드는 단순히 특징 이름(클래스 라벨들을 포함), 각각의 발생가능한 값들, 그리고 그 자신의 특징 벡터들을 읽는다. 그리고 위에 정의된 알맞은 데이터 구조를 채운다.

 

 

def TrainClassifier(self):
                for fv in self.featureVectors:
                        self.labelCounts[fv[len(fv)-1]] += 1 #udpate count of the label
                        for counter in range(0, len(fv)-1):
                                self.featureCounts[(fv[len(fv)-1], self.featureNameList[counter], fv[counter])] += 1
 
                for label in self.labelCounts:
                #increase label counts (smoothing). remember that the last feature is actually the label
                        for feature in self.featureNameList[:len(self.featureNameList)-1]:
                                self.labelCounts[label] += len(self.features[feature])

 

TrainClassifier 메서드는 단순히 각 특징 값과 클래스 라벨이 동시에 발생하는 횟수를 세고, 그것들을 3-튜플 형태로 저장한다. 이 카운트들은 add-one 평활화를 통해 자동적으로 평활화된다. 이 사전의 디폴트 카운트값은 '1'이다. 라벨들의 카운트들 또한 관찰의 총 횟수로 이 카운트들을 증가시킴으로써 조정된다.

 

 

def Classify(self, featureVector):      #featureVector is a simple list like the ones that we use to train
                probabilityPerLabel = {} #store the final probability for each class label
                for label in self.labelCounts:
                        logProb = 0
                        for featureValue in featureVector:
                                logProb += math.log(self.featureCounts[(label, 
                                                self.featureNameList[featureVector.index(featureValue)],
                                                featureValue)]/self.labelCounts[label])
                        probabilityPerLabel[label] =
                                                (self.labelCounts[label]/sum(self.labelCounts.values())) * math.exp(logProb)
                print probabilityPerLabel
                return max(probabilityPerLabel, key = lambda classLabel: probabilityPerLabel[classLabel])

 

마지막으로, 단일 특징 벡터(하나의 리스트)를 인수로 받아서 각 라벨마다 개별적인 (최대우도추정에 의해 평활화된) 조건부 확률들의 결과를 계산하는 Classify 메서드가 있다. 각 라벨들에 마지막으로 계산된 확률은 사전 'probabilitPerLabel'에 저장된다. 마지막줄에서 가장 높은 확률을 가진 probabilityPerLabel가 반환된다. Note that the multiplication is actually done as addition in the log domain as the numbers involved are extremely small. 또한,  이 클래스 라벨을 갖는 사전확률이 이 곱셈의 한 요소로 사용되었다.

 

다음은 테스트 메서드를 포함하는 전체 코드이다.

 

 

#Author: Krishnamurthy Koduvayur Viswanathan
 
from __future__ import division
import collections
import math
 
class Model: 
        def __init__(self, arffFile):
                self.trainingFile = arffFile
                self.features = {}      #all feature names and their possible values (including the class label)
                self.featureNameList = []       #this is to maintain the order of features as in the arff
                self.featureCounts = collections.defaultdict(lambda: 1)
                                                        #contains tuples of the form (label, feature_name, feature_value)
                self.featureVectors = []        #contains all the values and the label as the last entry
                self.labelCounts = collections.defaultdict(lambda: 0)   #these will be smoothed later
 
        def TrainClassifier(self):
                for fv in self.featureVectors:
                        self.labelCounts[fv[len(fv)-1]] += 1 #udpate count of the label
                        for counter in range(0, len(fv)-1):
                                self.featureCounts[(fv[len(fv)-1], self.featureNameList[counter], fv[counter])] += 1
 
                for label in self.labelCounts: 
                                #increase label counts (smoothing). remember that the last feature is actually the label
                        for feature in self.featureNameList[:len(self.featureNameList)-1]:
                                self.labelCounts[label] += len(self.features[feature])
 
        def Classify(self, featureVector):      #featureVector is a simple list like the ones that we use to train
                probabilityPerLabel = {}
                for label in self.labelCounts:
                        logProb = 0
                        for featureValue in featureVector:
                                logProb += math.log(self.featureCounts[(label,
                                                self.featureNameList[featureVector.index(featureValue)],
                                                featureValue)]/self.labelCounts[label])
                        probabilityPerLabel[label] =
                                        (self.labelCounts[label]/sum(self.labelCounts.values())) * math.exp(logProb)
                print probabilityPerLabel
                return max(probabilityPerLabel, key = lambda classLabel: probabilityPerLabel[classLabel])
                                
        def GetValues(self):
                file = open(self.trainingFile, 'r')
                for line in file:
                        if line[0] != '@':  #start of actual data
                                self.featureVectors.append(line.strip().lower().split(','))
                        else:   #feature definitions
                                if line.strip().lower().find('@data') == -1 and (not line.lower().startswith('@relation')):
                                        self.featureNameList.append(line.strip().split()[1])
                                        self.features[self.featureNameList[len(self.featureNameList) - 1]] =
                                                        line[line.find('{')+1: line.find('}')].strip().split(',')
                file.close()
 
        def TestClassifier(self, arffFile):
                file = open(arffFile, 'r')
                for line in file:
                        if line[0] != '@':
                                vector = line.strip().lower().split(',')
                                print "classifier: " + self.Classify(vector) + " given " + vector[len(vector) - 1]
		
if __name__ == "__main__":
        model = Model("/home/tennis.arff")
        model.GetValues()
        model.TrainClassifier()
        model.TestClassifier("/home/tennis.arff")

 

샘플 ARFF 파일을 다운로드해서 이것을 테스트 해 보라.tennis.arff

 

 

Update: 나는 GetValues() 함수의 끝에서 첫(번째) 줄에서 버그를 찾았다. 이 줄은 ARFF 파일에서 발생가능한 값을 가져오고 self.featureNameList에 저장한다. 이 메서드는 공백이 제대로 처리되지 않았다. 이 라인을 다음과 같이 고쳤다:

 

self.features[self.featureNameList[len(self.featureNameList) - 1]] = [featureName.strip() for featureName in line[line.find('{')+1: line.find('}')].strip().split(',')]

 

 

Posted by Kugi
,