본문 바로가기
Framework & Library/Spring Framework

[Spring Framework] : 자연어처리

by 오주현 2021. 11. 20.
반응형
자연어처리?

- 내가 말 하고 있는 단어, 타이핑하는 글 등을 컴퓨터가 인식할 수 있게 하는 걸 자연어처리라고 합니다.

 

- 자연어처리는 한국어로 사용하기엔 어렵습니다.

 

- 한국어 문장을 입력받아 한국어 어휘 형태 및 의미 와 문장의 구조 및 의미를 분석하여 언어를 이해하는 기술입니다.

 

- 형태소 분석은 언어 분석 기술 중 하나로 문장을 분해가능한 의미 최소단위로 분리하고 품사를 태깅합니다.

 

- 형태소 분석과 명사 분석은 가장 보편적으로 많이 사용된 분석 기술입니다.

 

- 언어 분석은 반드시 데이터사전이 필요하며, 데이터사전은 꾸준히 학습시켜야 합니다.

 

- 자연어처리는 주로 영어로 되어 있는데 영문학적으로 구조가 명확히 분류되어 있어서 (형태가 명확 1형식, 2형식, 품사가 명확하다.  띄어쓰기가 정확하다.) 자연어처리하기에 좋은 환경입니다.

 

- 반면에 한국어는 조사가 너무 많이 있고, 신조어도 많이 나오고 규칙이 명확하지 않기 때문에 외국에서 만든 기술로 한국어를 적용시킬 수 없다는 문제가 있습니다. 

 

- 이런 문제를 놔두고 한국어에도 적용시키기 위해 사용하는 것이 형태소분석입니다.

 

- Komoran은 자바에서 사용하는 형태소 분석기입니다.

 


pom.xml

	<repository>
	<id>jitpack.id</id>
	<url>https://jitpack.io</url>
	</repository>

오라클에서 메이븐 저장소에 제공을 하고 있지 않기 때문에, 메이븐 사설 저장소 사용합니다.

<repositories> 안에 넣어줍니다.

 

	<dependency>
	<groupId>com.github.shin285</groupId>
	<artifactId>KOMORAN</artifactId>
	<version>3.3.4</version>
	</dependency>

자연어 처리 - 형태소 분석기 (Komoran 다운로드를 위한 레파지토리 등록합니다.

<dependency>안에 넣어줍니다.


package poly.service;

import java.util.List;
import java.util.Map;

public interface IWordAnalysisService {

	//자연어 처리 - 형태소 분석 (명사만 추출하기)
	List<String> doWordNouns(String text) throws Exception;
	
	//빈도수 분석(단어별 출현 빈도수)
	Map<String, Integer> doWordCount(List<String> pList) throws Exception;
	
	//분석할 문장의 자연어 처리 및 빈도수 분석 수행
	Map<String, Integer> doWordAnalysis(String text) throws Exception;
	
	
}

IWordAnalysisService

 


package poly.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;

import kr.co.shineware.nlp.komoran.constant.DEFAULT_MODEL;
import kr.co.shineware.nlp.komoran.core.Komoran;
import kr.co.shineware.nlp.komoran.model.KomoranResult;
import poly.service.IWordAnalysisService;
import poly.util.CmmUtil;

@Service("WordAnalysisService")
public class WordAnalysisService implements IWordAnalysisService {

	private Logger log = Logger.getLogger(this.getClass());
	
	//자연어 처리 - 형태소 분석기인 Komoran를 메모리에 올리기 위해 WordAnalysisService 클래스 내 전역 변수로 설정합니다.
	Komoran nlp = null;
	
	//생성자 사용함 - 톰켓에서 부팅할 때 @Service를 모두 메모리에 올립니다.
	//톰켓이 메모리에 올릴 때, 생성자에 선언한 Komoran도 같이 메모리에 올라가도록 생성자에 코딩합니다.
	//생성자에서 Komoran을 메모리에 올리면, 매번 메모리에 올려서 호출하는 것이 아니라, 
	// 메모리에 올리간 객체만 불러와서 사용할 수 있기 때문에 처리 속도가 빠릅니다.
	public WordAnalysisService() {
		
		log.info(this.getClass().getName() + ".WordAnalysisService creator Start !");
		
		//NLP 분석 객체 메모리 로딩합니다.
		this.nlp = new Komoran(DEFAULT_MODEL.LIGHT); // 학습데이터 경량화 버전( 웹 서비스에 적합합니다. )
		//this.nlp = new Komoran(DEFAULT_MODEL.FULL); // 학습데이터 전체 버전(일괄처리 : 배치 서비스에 적합합니다.)
	
		log.info("난 톰켓이 부팅되면서 스프링 프렝미워크가 자동 실행되었고, 스프링 실행될 때 nlp 변수에 Komoran 객체를 생성하여 저장하였다.");
		
		log.info(this.getClass().getName() + ".WordAnalysisService creator End !");
	
	
	}
	
	@Override
	public List<String> doWordNouns(String text) throws Exception {

		log.info(this.getClass().getName() + ".doWordAnalysis Start !");
		
		log.info("분석할 문장 : " + text);
		
		//분석할 문장에 대해 정제(쓸데없는 특수문자 제거)
		String replace_text = text.replace("[^가-힣a-zA-Z0-9", " ");
		
		log.info("한국어, 영어, 숫자 제외 단어 모두 한 칸으로 변환시킨 문장 : " + replace_text);
		
		//분석할 문장의 앞, 뒤에 존재할 수 있는 필요없는 공백 제거
		String trim_text = replace_text.trim();
		
		log.info("분석할 문장 앞, 뒤에 존재할 수 있는 필요 없는 공백 제거 : " + trim_text);
		
		//형태소 분석 시작
		KomoranResult analyzeResultList = this.nlp.analyze(trim_text);
		
		//형태소 분석 결과 중 명삼나 가져오기
		List<String> rList = analyzeResultList.getNouns();
		
		if (rList == null) {
			rList = new ArrayList<String>();
		}
		
		//분석 결과 확인을 위한 로그 찍기
		Iterator<String> it = rList.iterator();
		
		while (it.hasNext()) {
			//추출된 명서
			String word = CmmUtil.nvl(it.next());
			
			log.info("word : " + word);
		}
		
		
		log.info(this.getClass().getName() + ".doWordAnalysis End !");
		
		return rList;
	}

	@Override
	public Map<String, Integer> doWordCount(List<String> pList) throws Exception {

		log.info(this.getClass().getName() + ".doWordCount Start !");
		
		if (pList ==null) {
			pList = new ArrayList<String>();
		}
		
		//단어 빈도수(사과, 3) 결과를 저장하기 위해 Map객체 생성합니다.
		Map<String, Integer> rMap = new HashMap<>();
		
		//List에 존재하는 중복되는 단어들의 중복제거를 위해 set 데이터타입에 데이터를 저장합니다.
		//rSet 변수는 중복된 데이터가 저장되지 않기 떄문에 중복되지 않은 단어만 저장하고 나머지는 자동 삭제합니다.
		Set<String> rSet = new HashSet<String>(pList);
		
		//중복이 제거된 단어 모음에 빈도수를 구하기 위해 반복문을 사용합니다.
		Iterator<String> it = rSet.iterator();
		
		while(it.hasNext()) {
			//중복 제거된 단어
			String word = CmmUtil.nvl(it.next());
			
			//단어가 중복 저장되어 있는 pList로부터 단어의 빈도수 가져오기
			int frequency = Collections.frequency(pList, word);
			
			log.info("word :" + word);
			log.info("frequency : " + frequency);
			
			rMap.put(word, frequency);
		}
		
		log.info(this.getClass().getName() + ".doWordCount End !");
		
		return rMap;
	}

	@Override
	public Map<String, Integer> doWordAnalysis(String text) throws Exception {

		
	//문장의 명사를 추출하기 위한 형태소 분석 실행
		List<String> rList = this.doWordNouns(text);
		
		if(rList == null) {
			rList = new ArrayList<String>();
		}
		
		//추출된 명사 모음(리스트)의 명사 단어별 빈도수 계산
		Map<String, Integer> rMap = this.doWordCount(rList);
		
		if(rMap == null) {
			rMap = new HashMap<String, Integer>();
		}
		
		return rMap;
	}

}

WordAnalysisService

 

주석을 참고하면 됩니다.

 


package poly.controller;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import poly.service.IWordAnalysisService;

@Controller("WordController")
public class WordController {

	private Logger log = Logger.getLogger(this.getClass());
	
	@Resource(name="WordAnalysisService")
	private IWordAnalysisService wordAnalysisService;
	
	@RequestMapping(value = "word/analysis")
	@ResponseBody
	public Map<String, Integer> analysis() throws Exception {
		
		log.info(this.getClass().getName() + ".inputForm !");
		
		//분석할 문장
		String text = "아침에 밥을 꼭 먹고 점심엔 점심 밥을 꼭 먹고 저녁엔 저녁 밥을 꼭 먹자!";
		
		//신조어 및 새롭게 생겨난 가수 및 그룹명은 제대로 된 분석이 불가능합니다.
		// 새로운 명사 단어들은 어떻게 데이터를 처리해야 할까?? => 데이터사전의 주기적인 업데이트

		Map<String, Integer> rMap = wordAnalysisService.doWordAnalysis(text);
		
		if(rMap == null) {
			rMap = new HashMap<String, Integer>();
		}
		
		
		return rMap;
	}
}

WordController

 

주석을 참고하면 됩니다.


서버를 돌려줍니다.

 

중복 단어 체크와 단어 분석이 완료되었습니다.

 

특수 문자 역시 제거가 되어 나왔습니다.

반응형

댓글