Hadoop 기반의 소박한 베일스 알고리즘 실현

10487 단어 Hadoop
베일스 분류기의 분류 원리는 특정한 대상의 선험 확률을 통해 베일스 공식을 이용하여 후험 확률을 계산하는 것이다. 즉, 이 대상은 특정한 유형에 속하는 확률로 가장 큰 후험 확률을 가진 유형을 이 대상이 속하는 유형으로 선택한다.   
다음은 간단한 예입니다.
데이터: 날씨 상황과 매일 축구를 하는지 여부의 기록표
일자
축구를 하다
날씨 명사
온도.
습도
풍속
1일
아니오(0)
맑은 날(0)
열(0)
높음(0)
낮음(0)
2일
아니오(0)
맑은 날(0)
열(0)
높음(0)
높음(1)
3일
예(1)
구름이 많음(1)
열(0)
높음(0)
낮음(0)
4번
예(1)
비가 와요(2)
편안함(1)
높음(0)
낮음(0)
5일
예(1)
비가 와요(2)
시원하다(2)
정상(1)
낮음(0)
6일
아니오(0)
비가 와요(2)
시원하다(2)
정상(1)
높음(1)
7일
예(1)
구름이 많음(1)
시원하다(2)
정상(1)
높음(1)
8번
아니오(0)
맑은 날(0)
편안함(1)
높음(0)
낮음(0)
9일
예(1)
맑은 날(0)
시원하다(2)
정상(1)
낮음(0)
10일
예(1)
비가 와요(2)
편안함(1)
정상(1)
낮음(0)
11일
예(1)
맑은 날(0)
편안함(1)
정상(1)
높음(1)
12일
예(1)
구름이 많음(1)
편안함(1)
높음(0)
높음(1)
13일
예(1)
구름이 많음(1)
열(0)
정상(1)
낮음(0)
14일
아니오(0)
비가 와요(2)
편안함(1)
높음(0)
높음(1)
15일
?
맑은 날(0)
시원하다(2)
높음(0)
높음(1)
15일, 이런 날씨에 축구를 할지 안 할지를 예측해야 한다.
가령 15일에 공을 차러 가면 공을 차는 확률을 계산하는 과정은 다음과 같다.
P(킥 확률) = 9/14
P(맑은 날|차기) = 킥 일수 중 맑은 날 킥 횟수/킥 횟수 = 2/9
P(시원|킥) = 킥 일수 중 시원한 킥 횟수/킥 횟수 = 3/9
P(습도 높음|킥) = 킥 일수 중 습도 높음 킥/킥 횟수 = 3/9
P(풍속높음|차기) = 킥 일수 중 풍속높음 킥 횟수/킥 횟수 = 3/9
15번 킥 확률 P = 9/14 * 2/9 * 3/9 * 3/9 * 3/9 * 3/9 = 0.00529
상기 절차에 따라 15번 축구를 하지 않을 확률을 계산할 수 있다. P = 5/14 * 3/5 * 1/5 * 4/5 * 3/5 = 0.02057
15일에 축구를 하지 않을 확률이 축구를 하지 않을 확률보다 높다는 것을 알 수 있다. 15일에 축구를 하지 않을 확률이 높다는 것을 예측할 수 있다.
박소 베일스의 흐름을 이해한 뒤 MR 프로그램을 설계하기 시작했다.Mapper에서 트레이닝 데이터를 분리한다. 즉, 이 트레이닝 데이터를 분류와 트레이닝 데이터로 분리하고 트레이닝 데이터를 사용자 정의 값 형식으로 저장한 다음에 Reducer에 전달한다.
    Mapper:
public class BayesMapper extends Mapper {
	Logger log = LoggerFactory.getLogger(BayesMapper.class);
	private IntWritable myKey = new IntWritable();
	private MyWritable myValue = new MyWritable();
	@Override
	protected void map(Object key, Text value, Context context)
			throws IOException, InterruptedException {
		log.info("***"+value.toString());
		int[] values = getIntData(value);
		int label = values[0];  // 
		int[] result = new int[values.length-1]; // 
		for(int i =1;i
    MyWritable:
public class MyWritable implements Writable{
	private int[] value;
	public MyWritable() {
		// TODO Auto-generated constructor stub
	}
	public MyWritable(int[] value){
		this.setValue(value);
	}
	@Override
	public void write(DataOutput out) throws IOException {
		out.writeInt(value.length);
		for(int i=0; i

Reducer에서 setup에서 테스트 데이터를 초기화해야 한다. 트레이닝 데이터와 테스트 데이터의 속성은 모두 0,1 두 가지 값만 있기 때문에 Reduce에서 같은 유형의 서로 다른 속성의 값의 합(즉 1이 나타난 횟수를 통계하고 0의 횟수는 이 유형의 데이터의 총계로 1이 나타난 횟수를 줄이는 것이다)을 통계해야 한다.대상 CountAll을 사용하여 현재 클래스 k, 클래스 k에서 각 속성에 1이 나타날 확률, 클래스 k에서 데이터의 개수를 저장한 다음에 cleanup에서 현재 테스트 데이터에 어떤 종류가 나타날 확률이 가장 높은지 계산하고 이 클래스를 현재 테스트 데이터의 클래스로 설정합니다.
public class BayesReducer extends Reducer{
	Logger log = LoggerFactory.getLogger(BayesReducer.class);
	private String testFilePath;
	//  
	private ArrayList testData = new ArrayList<>();
	//  k 
	private ArrayList allData = new ArrayList<>();

	@Override
	protected void setup(Context context)
			throws IOException, InterruptedException {
		Configuration conf = context.getConfiguration();
		testFilePath = conf.get("TestFilePath");
		Path path = new Path(testFilePath);
		FileSystem fs = path.getFileSystem(conf);
		readTestData(fs,path);
	}
	/***
	 * k,v => 0  {{0,1,0,1,0,0,1,...},{},{},...}
	 */
	@Override
	protected void reduce(IntWritable key, Iterable values,
			Context context)
			throws IOException, InterruptedException {
		Double[] myTest = new Double[testData.get(0).length-1];
		for(int i=0;i labelG = new HashMap<>();
		Long allSum = getSum(allData); // 
		for(int i=0; i allData2) {
		Long allSum = 0L;
		for (CountAll countAll : allData2) {
			log.info(" :"+countAll.getK()+" :"+myString(countAll.getValue())+" :"+countAll.getSum());
			allSum += countAll.getSum();
		}
		return allSum;
	}
	/***
	 *  
	 * @param test
	 * @param labelG
	 * @return
	 */
	private int getClasify(int[] test,HashMap labelG ) {
		double[] result = new double[allData.size()]; // 
		for(int i = 0; i maxValue){
//				maxValue = result[i];
//				index = i;
//			}
//		}
//		return allData.get(index).getK();
		if(result[0] > result[1]){
			return 0;
		}else{
			return 1;
		}
	}
	/***
	 *  
	 * @param fs
	 * @param path
	 * @throws NumberFormatException
	 * @throws IOException
	 */
	private void readTestData(FileSystem fs, Path path) throws NumberFormatException, IOException {
		FSDataInputStream data = fs.open(path);
		BufferedReader bf = new BufferedReader(new InputStreamReader(data));
		String line = "";
		while ((line = bf.readLine()) != null) {
			String[] str = line.split(",");
			int[] myData = new int[str.length];
			for(int i=0;i
    CountAll:
public class CountAll {
	private Long sum;
	private Double[] value;
	private int k;
	public CountAll(){}
	public CountAll(Long sum, Double[] value,int k){
		this.sum = sum;
		this.value = value;
		this.k = k;
	}
	public Double[] getValue() {
		return value;
	}
	public void setValue(Double[] value) {
		this.value = value;
	}
	public Long getSum() {
		return sum;
	}
	public void setSum(Long sum) {
		this.sum = sum;
	}
	public int getK() {
		return k;
	}
	public void setK(int k) {
		this.k = k;
	}
}

Reducer의 getClassify 방법에서 알 수 있듯이 P(ai|c)=0의 경우(ai는 속성, c는 분류)를 고려하여 라프라스 매끄러움을 이용하여 이 문제를 해결했다. 즉, Reduce에서 속성 값과 그룹 myTest를 초기화할 때 그 중의 각 원소의 값을 1로 설정하고 이 분류의 데이터 총수sum를 2로 초기화하면 된다.
P(a1|c)*P(a2|c)를 계산하는 중이라서...*P(an|c)시 모든 p값이 비교적 작으면 속성이 많을 때 정밀도 손실이 발생한다. 즉, 마지막으로 모든 종류가 계산한 확률은 0이다.여기서 대수를 ln(P(a1|c)*P(a2|c)로 변환합니다...*P(an|c)) = ln(P(a1|c)) + ln(P(a2|c)) + ...+ln(P(an|c)) - 정밀도 손실을 피할 수 있습니다.
    
트레이닝 데이터 중 일부는 다음과 같습니다.
1,0,0,0,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0
1,0,0,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,1
1,1,0,1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1
1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0
1,0,0,0,1,0,0,0,0,1,0,0,0,1,1,0,1,0,0,0,1,0,1
1,1,0,1,1,0,0,0,1,0,1,0,1,1,0,0,0,0,0,0,0,1,1
1,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1
1,0,0,1,0,0,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,1,1
1,0,1,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0
1,1,1,0,0,1,0,1,0,0,1,1,1,1,0,0,1,1,1,1,1,0,1
1,1,1,0,0,1,1,1,0,1,1,1,1,0,1,0,0,1,0,1,1,0,0
1,1,0,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,1
1,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,1
1,1,0,1,1,0,0,1,1,1,0,1,1,1,1,1,1,0,1,1,0,1,1
1,0,1,1,0,0,1,1,1,0,0,0,1,1,0,0,1,1,1,0,1,1,1
1,0,0,1,1,0,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,1,0
1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
1,1,0,1,0,1,0,1,1,0,1,0,1,1,0,0,0,1,0,0,1,1,0
1,1,0,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0
1,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1
1,1,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,0,0,1,1,0,0
1,1,1,0,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,1,0,0,0
1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0
1,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0
검증 데이터의 일부는 다음과 같습니다.
1,1,0,0,1,1,0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,0,0
1,1,0,0,1,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0
1,0,0,0,1,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,0,0,1
1,0,1,1,1,0,0,1,0,1,0,0,1,1,1,0,1,0,0,0,0,1,0
1,0,0,1,0,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1
1,0,0,1,1,0,1,0,0,1,0,1,0,1,0,0,1,0,0,0,0,1,1
1,1,0,0,1,0,0,1,1,1,1,0,1,1,1,0,1,0,0,0,1,0,1
1,1,0,0,1,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0
1,1,0,0,1,1,1,0,0,1,1,1,0,0,1,0,1,1,0,1,0,0,0
1,1,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0,0,0,1,0,0,0
1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0
1,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1
1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0
1,1,0,0,1,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,1,0,0
1,1,0,1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0,1,0
1,1,1,0,0,1,1,1,1,0,1,1,1,1,0,0,0,1,0,0,0,1,1
1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,0,0,1,0,0,0,0,1
1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0
1,1,1,1,0,1,0,1,1,0,1,0,1,1,0,0,1,0,0,0,1,1,0
1,1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,0
1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,1,1,1
1,0,0,1,1,1,0,0,1,1,1,0,0,1,1,1,1,0,1,0,1,1,0
1,1,1,0,1,1,1,1,0,0,0,1,1,0,0,0,1,1,0,0,1,0,0
1,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0
1,1,1,0,0,1,1,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0
1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,0
1,1,1,1,1,0,1,1,1,0,1,0,0,1,1,1,1,0,0,1,1,0,0

훈련 데이터는 86개, 검증 데이터는 184개였기 때문에 최종 예측의 정확도는 0.8이었다.훈련 데이터와 검증 데이터를 바꾸면 예측 결과의 정확도가 더욱 높아진다.

좋은 웹페이지 즐겨찾기