'ITWeb/검색일반'에 해당되는 글 17건

  1. 2017.06.09 [Arirang Analyzer - lucene 6.5.0] Term startOffset 정렬 오류
  2. 2017.06.09 [Arirang] first position increment must be > 0 오류
  3. 2017.05.31 [검색] SEO 태그 가이드
  4. 2017.01.19 [Lucene]
  5. 2016.10.21 [OCR] 광학문자인식 정보
  6. 2016.10.12 [우편번호검색] 우편번호검색 서비스를 만들기 위한 기본 정보
  7. 2016.06.27 [Javascript] English to Korean (영문 한글 전환)
  8. 2016.04.27 [검색이론] Recall 과 Precision - wikipedia
  9. 2016.03.30 [Lucene] TermVector 정보 중 Offset 에 대해서.
  10. 2016.02.29 [Maven] maven 프로젝트 naming convention - group id, artifact id, version ...

[Arirang Analyzer - lucene 6.5.0] Term startOffset 정렬 오류

ITWeb/검색일반 2017.06.09 10:23

[arirang-analyzer-6.5.0]

  term analyzed 시 startOffset 정보에 대한 정렬이 역전 되는 오류

  개별 term 에서의 startOffset 이 역전 되기 때문에 아래 class 의 method 에서 정렬을 다시 맞춰줍니다.

  (정상적인 방법 이라기 보다는 일단 문제를 회피하기 위한 방법 입니다.)


  Class : KoreanFilter

  Method 1 :

    private void analysisKorean(String input) throws MorphException {


  //  input = trimHangul(input);

      List<AnalysisOutput> outputs = morph.analyze(input);

      if (outputs.size() == 0) {

        return;

      }


      Map<String, KoreanToken> map = new LinkedHashMap<String, KoreanToken>();

      if (hasOrigin) {

        map.put("0:" + input, new KoreanToken(input, offsetAtt.startOffset()));

      }


      extractKeyword(outputs, offsetAtt.startOffset(), map, 0);


      Collection<KoreanToken> values = map.values();

      for (KoreanToken kt : values) {

        kt.setOutputs(outputs);

      }


      // 이 부분에서 map 에 등록된 정보를 정렬 합니다.


      morphQueue.addAll(map.values());

    }


  Method 2 :

    private void analysisKorean(String input) throws MorphException {


  //  input = trimHangul(input);

      List<AnalysisOutput> outputs = morph.analyze(input);

      if (outputs.size() == 0) {

        return;

      }


      Map<String, KoreanToken> map = new LinkedHashMap<String, KoreanToken>();

      if (hasOrigin) {

        map.put("0:" + input, new KoreanToken(input, offsetAtt.startOffset()));

      }


      extractKeyword(outputs, offsetAtt.startOffset(), map, 0);


      Collection<KoreanToken> values = map.values();

      for (KoreanToken kt : values) {

        kt.setOutputs(outputs);

      }


      morphQueue.addAll(map.values());

      // 이 부분에서 morphQueue 에 등록된 정보를 정렬 합니다.

      morphQueue.sort(Comparator.comparingInt(KoreanToken::getOffset));

    }


  Method 3 : 

    protected void extractKeyword(List<AnalysisOutput> outputs, int startoffset,

      final Map<String, KoreanToken> map, int position) {

      ... 원본 코드 생략


      // 이 부분에서 map 에 대한 등록된 정보를 정렬 합니다.

    }


  정렬 방법 :

    참고) https://stackoverflow.com/questions/109383/sort-a-mapkey-value-by-values-java


    final List<Map.Entry<String, KoreanToken>> offsetSorts = map.entrySet().stream()

        .sorted(Map.Entry.comparingByValue(Comparator.comparingInt(KoreanToken::getOffset)))

        .collect(Collectors.toList());


    map.clear();


    offsetSorts.stream().forEachOrdered(e -> map.put(e.getKey(), e.getValue()));


  Method 4 :

    KoreanFilter 를 상속받아 CustomKoreanFilter 를 만들어 사용 하면 됩니다.


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

[Arirang] first position increment must be > 0 오류

ITWeb/검색일반 2017.06.09 10:22

아직 확인 및 테스트 하지 않았습니다. ^^;

그냥 코드만 보고 이렇게 하면 되겠다 정도만 입니다.


- DefaultIndexingChain.java

first position increment must be > 0


관련 에러 수정을 위해서는 KoreanFilter.java 내 posIncrAtt.setPositionIncrement(iw.getPosInc()); 영역에서 

iw.getPosInc() 가 -1 인지 검사해서 1로 변경을 해줍니다.

변경에 따른 오류에 대해서 검토가 필요 합니다.


  private void setAttributesFromQueue(boolean isFirst) {

    final KoreanToken iw = morphQueue.removeFirst();

    if (isFirst && !morphQueue.isEmpty()) {

      // our queue has more elements remaining (e.g. we decompounded)

      // capture state for those. We set the term attribute to be empty

      // so we save lots of array copying later.

      termAtt.setEmpty();

      currentState = captureState();

    }

 

    termAtt.setEmpty().append(iw.getTerm());

    offsetAtt.setOffset(iw.getOffset(), iw.getOffset() + iw.getLength());

    morphAtt.setToken(iw);


    // on the first Token we preserve incoming increment:

    if (!isFirst) {

      posIncrAtt.setPositionIncrement(iw.getPosInc());

    }

    

    String type = TokenUtilities.getType(iw.getTerm().toCharArray(), iw.getTerm().length());

    typeAtt.setType(type);

    

    // TODO: How to handle PositionLengthAttribute correctly?

  }



저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

[검색] SEO 태그 가이드

ITWeb/검색일반 2017.05.31 16:52

- 네이버 검색 관련 가이드 : http://webmastertool.naver.com/guide/basic_optimize.naver

- 구글 검색 관련 가이드 : https://developers.google.com/search/docs/guides/search-gallery 

- 페이스북 오픈 그래프 태그 가이드 : https://developers.facebook.com/docs/sharing/webmasters#markup

- 페이스북 앱 링크 태그 가이드 : https://developers.facebook.com/docs/applinks/metadata-reference


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
tags : search, SEO, tag, 검색
Trackback 0 : Comment 0

[Lucene]

ITWeb/검색일반 2017.01.19 18:41
아주 기초적인 것도 잊어버리는 것 같아 기록해 봅니다.

Multi-value fields and the inverted index

The fact that all field types support multi-value fields out of the box is a consequence of the origins of Lucene. Lucene was designed to be a full text search engine. In order to be able to search for individual words within a big block of text, Lucene tokenizes the text into individual terms, and adds each term to the inverted index separately.

This means that even a simple text field must be able to support multiple values by default. When other datatypes were added, such as numbers and dates, they used the same data structure as strings, and so got multi-values for free.


이 글은 아래 elasticsearch 에서 퍼왔습니다.


[문서]

https://www.elastic.co/guide/en/elasticsearch/reference/2.4/array.html

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

[OCR] 광학문자인식 정보

ITWeb/검색일반 2016.10.21 13:01

조만간 사용을 해야해서 일단 링크 투척


https://github.com/tesseract-ocr/tesseract

https://github.com/jflesch/pyocr



opencv 도 링크 투척


http://docs.opencv.org/2.4.9/modules/refman.html


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

[우편번호검색] 우편번호검색 서비스를 만들기 위한 기본 정보

ITWeb/검색일반 2016.10.12 17:47


우편번호 검색 기능을 구현 하다 보니 우체국 DB로는 daily 변경 데이터 반영이 어려워 그냥 행자부 DB를 가지고 만드는게 좋겠다는 결론을 내렸습니다.


방법은)

1. 우체국 DB + 행자부 변경 DB to 우체국 DB

2. 행자부 DB + 행자부 변경 DB


================================


우편번호 검색 서비스를 만들기 위해서 추려본 내용입니다.

그냥 편하게 행자부나 우체국에서 제공하는 오픈API를 사용해도 되지만 직접 구축해보고 싶어하는 분들을 위해 공유해 봅니다.


[도로명주소 오픈API]

https://www.juso.go.kr/addrlink/devAddrLinkRequestSample.htm


[우체국 오픈API]

http://biz.epost.go.kr/customCenter/custom/custom_10.jsp


[우체국 DB 다운로드]

http://www.epost.go.kr/search/zipcode/cmzcd002k01.jsp


[도로/지번 주소와 우편번호]

  도로/지번 주소는 행정자치부에서 관리

  우편번호는 행자부 데이터를 받아와서 우체국에서 매칭 및 생성 관리 (일부 데이터는 누락 될 수 있음)


[우편번호 PK]

  건물관리번호


[도로명]

  우편번호 / 시도(영문) / 시군구(영문) / 읍면(영문) / 도로명(영문) / 지하여부 /  건물번호본 - 건물번호부 / (법정동명, 리명, 시군구동건물명)

    지하여부 : 1 일 경우 지하 + 건물번호본 - 건물번호부 / (법정동명, 리명, 시군구동건물명) 로 지정 합니다. (지하 31)


[지번]

  우편번호 / 시도(영문) / 시군구(영문) / 읍면(영문) / 법정동명 / 리명 / 산여부 / 지번본번 - 지번부번 / (시군구동건물명)

    산여부 : 1 일 경우 산 + 지번본번 - 지번부번 / (시군구동건물명) 로 지정 합니다. (산109-7)


[사서함]

  우편번호 / 시도 / 시군구 / 읍면 / 사서함명 / 시작사서함주번호 - 시작사서함부번호 / 끝사서함주번호 - 끝사서함부번호

    사서함번호는 시작부터-끝까지 (전체) 로 표기 합니다. (1-2500 (전체))


[도로명 범위]

  우편번호 / 시도(시도영문) / 시군구(시군구영문) / 읍면(영문) / 도로명(영문) / 지하여부 / 시작건물번호(주) - 시작건물번호(부) / 끝건물번호(주) - 끝건물번호(부)

    시작건물번호-끝건물번호 표현

      382, 0 - 384, 1 = 382, 383, 384, 384-1


[지번 범위]

  우편번호 / 시도(영문) / 시군구(영문) / 읍면동(영문) / 리명 / 산여부 / 시작주번지 - 시작부번지 / 끝주번지 - 끝부번지


[인덱스 종류]

  통합주소 DB

  사서함 + 도로명 범위 + 지번 범위 DB


[검색 대상 필드]

  도로명(법정동명 + 리명) + 건물번호 

  읍/면/동/리 + 지번

  건물명 (시군구동건물명)

  사서함 + 사서함번호


[통합검색]

  시도 / 시군구 / 읍면 / 도로명 / 법정동명 / 리명 / 건물번호본 / 건물번호부 / 지번본번 / 지번부번 / 시군구동건물명


[화면출력]

  우편번호 / 시도(영문) / 시군구(영문) / 읍면(영문) / 도로명(영문) / 지하여부 /  건물번호본 - 건물번호부 / (법정동명, 리명, 시군구동건물명)

  우편번호 / 시도(영문) / 시군구(영문) / 읍면(영문) / 법정동명 / 리명 / 산여부 / 지번본번 - 지번부번 / (시군구동건물명)

  우편번호 / 시도 / 시군구 / 읍면 / 사서함명 / 시작사서함주번호 - 시작사서함부번호 / 끝사서함주번호 - 끝사서함부번호

  우편번호 / 시도(시도영문) / 시군구(시군구영문) / 읍면(영문) / 도로명(영문) / 지하여부 / 시작건물번호(주) - 시작건물번호(부) / 끝건물번호(주) - 끝건물번호(부)

  우편번호 / 시도(영문) / 시군구(영문) / 읍면동(영문) / 리명 / 산여부 / 시작주번지 - 시작부번지 / 끝주번지 - 끝부번지


[정렬]

  시도 / 시군구 / 우편번호 / score


[필터]

  시도 / 시군구


위 내용에서 입맛에 맞게 고치시면 됩니다.

꼭 저렇게 해야 한다는 것이 절대 아닙니다.

요구사항에 맞게 수정해서 만드시면 되겠습니다. :) 

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

[Javascript] English to Korean (영문 한글 전환)

ITWeb/검색일반 2016.06.27 23:51

필요해서 구글링으로 퍼왔습니다.

기본적으로는 한글 자모 분리 기능 구현을 사용한다고 보시면 됩니다.


아래 코드 중 구글링으로 퍼온 코드에서 읽기 쉽도록 약간의 변수명등 수정을 했습니다.

자바스크립트에서 function 선언에 대한 기본 이해를 하시면 코드 보기가 더 쉽습니다.


<html>

<head></head>

<script>

var convertEngToKor = function(args) {

var engChosung = "rRseEfaqQtTdwWczxvg"

var engChosungReg = "[" + engChosung + "]";

var engJungsung = {k:0,o:1,i:2,O:3,j:4,p:5,u:6,P:7,h:8,hk:9,ho:10,hl:11,y:12,n:13,nj:14,np:15,nl:16,b:17,m:18,ml:19,l:20};

var engJungsungReg = "hk|ho|hl|nj|np|nl|ml|k|o|i|O|j|p|u|P|h|y|n|b|m|l";

var engJongsung = {"":0,r:1,R:2,rt:3,s:4,sw:5,sg:6,e:7,f:8,fr:9,fa:10,fq:11,ft:12,fx:13,fv:14,fg:15,a:16,q:17,qt:18,t:19,T:20,d:21,w:22,c:23,z:24,x:25,v:26,g:27};

var engJongsungReg = "rt|sw|sg|fr|fa|fq|ft|fx|fv|fg|qt|r|R|s|e|f|a|q|t|T|d|w|c|z|x|v|g|";

var regExp = new RegExp("("+engChosungReg+")("+engJungsungReg+")(("+engJongsungReg+")(?=("+engChosungReg+")("+engJungsungReg+"))|("+engJongsungReg+"))","g");


var converter = function (args, cho, jung, jong) {

return String.fromCharCode(engChosung.indexOf(cho) * 588 + engJungsung[jung] * 28 + engJongsung[jong] + 44032);

};

var result = args.replace(regExp, converter);

console.log(result);

return result;

}


function run(engStr) {

convertEngToKor(engStr);

}


var convertEngToKor2 = (function () {

var engChosung = "rRseEfaqQtTdwWczxvg"

var engChosungReg = "[" + engChosung + "]";

var engJungsung = {k:0,o:1,i:2,O:3,j:4,p:5,u:6,P:7,h:8,hk:9,ho:10,hl:11,y:12,n:13,nj:14,np:15,nl:16,b:17,m:18,ml:19,l:20};

var engJungsungReg = "hk|ho|hl|nj|np|nl|ml|k|o|i|O|j|p|u|P|h|y|n|b|m|l";

var engJongsung = {"":0,r:1,R:2,rt:3,s:4,sw:5,sg:6,e:7,f:8,fr:9,fa:10,fq:11,ft:12,fx:13,fv:14,fg:15,a:16,q:17,qt:18,t:19,T:20,d:21,w:22,c:23,z:24,x:25,v:26,g:27};

var engJongsungReg = "rt|sw|sg|fr|fa|fq|ft|fx|fv|fg|qt|r|R|s|e|f|a|q|t|T|d|w|c|z|x|v|g|";

var regExp = new RegExp("("+engChosungReg+")("+engJungsungReg+")(("+engJongsungReg+")(?=("+engChosungReg+")("+engJungsungReg+"))|("+engJongsungReg+"))","g");


var converter = function (args, cho, jung, jong) {

return String.fromCharCode(engChosung.indexOf(cho) * 588 + engJungsung[jung] * 28 + engJongsung[jong] + 44032);

};


return (function (args) {

var result = args.replace(regExp, converter); 

console.log(result);

return result; 

});

})();


function run2(engStr) {

convertEngToKor2(engStr);

}

</script>

<body>

<input id="eng" value="skdlzl">

<button onclick="run(document.getElementById('eng').value)">run</button>

<button onclick="run2(document.getElementById('eng').value)">run2</button>

</body>

</html>


코드를 보시면 아시겠지만, 영문으로 작성한 skdlrl(나이키) 를 한글 나이키로 변환해서 리턴해 주도록 해줍니다.

보통 검색에서 자동완성 기능 구현 시 client side 에서 한영변환에 대한 기능으로 활용하기 위해 사용 합니다.


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

[검색이론] Recall 과 Precision - wikipedia

ITWeb/검색일반 2016.04.27 11:15

그냥 복습 차원에서 위키피디아에 있는 내용을 그대로 작성해 본 것입니다.


기본 IR 이론)

recall = Number of relevant documents retrieved / Total number of relevant documents

precision = Number of relevant documents retrieved / Total number of documents retrieved


원문링크)

https://ko.wikipedia.org/wiki/%EC%A0%95%EB%B0%80%EB%8F%84%EC%99%80_%EC%9E%AC%ED%98%84%EC%9C%A8



Precision, 정밀도 라고 되어 있는데 저는 그냥 정확도 라고 부릅니다.

이유는 뭐 별거 없고 말이 이게 더 쉽게 전달 되는것 같아서 이구요.

얼마나 관련(relevant) 있는 문서들이 나왔는지를 보는 지표 라서 그렇게 부릅니다.


Precision =  | {relevant documents} ∩ {retrieved documents} | / | {retrieved documents} |

정확도 = (관련문서 수  ∩  검색된 문서 수) / 검색된 문서 수


Recall, 이건 재현율 이라고 부릅니다.

precision 과는 약간 상충 되는 내용이기도 합니다.

그래서 둘 다 높히기는 참 어려운 것 같습니다.

이것은 관련(relevant) 있는 문서들 중 실제로 검색된 문서들의 비율이 됩니다.


Recall = | {relevant documents} ∩ {retrieved documents} | / | {relevant documents} |


두 개의 차이는 분모 부분이 다르다는 것입니다.

이 정보들은 실제 통계학에서도 동일하게 사용 됩니다.


False Positive/Negative

True Positive/Negative


음... 사실 저는 과거에 스팸 필터 엔진 만들때 사용하던 내용이였는데요.

스팸으로 표현 하면 스팸 문서가 아닌데 스팸 문서라고 하는게 false positive, 서버 장애가 아닌데 장애라고 하는 것도 같은 의미 입니다. 이런건 false alarm 이라고도 합니다.


false negative 는 false positive 와 반대겠죠.

스팸 문서 인데 스팸 문서가 아니라고 하는 것입니다. 실제 서버는 장애가 났는데 장애 알람이 오지 않은 경우가 되겠습니다.


그럼 true positive는 무엇일까요? 이건 그냥 정상 입니다.

스팸 문서를 스팸 문서라고 하는 것이구요. true negative 는 그렇습니다. 스팸 문서가 아닌걸 스팸문서가 아니라고 하는 것이 되겠습니다.


검색으로 풀면 )

 

 관련된 문서를

 관련 안된 문서를

 관련된 문서라고 함

 True Positive (TP)

 False Positive (FP)

 관련 안된 문서라고 함

 False Negative (TN)

 True Negative (TN)


통계적 관점에서의 계산 식은 아래와 같습니다.


Precision(Positive predictive value:PPV) = TP / (TP + FP)


Recall(Sensitivity) = TP / (TP + FN)


True Negative Rate(Specificity) = TN / (TN + FP)


Accuracy = (TP + TN) / (TP + TN + FP + FN)


여기까지 복습 차원에서 정리해 봤습니다.


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
tags : IR, precision, recall
Trackback 0 : Comment 0

[Lucene] TermVector 정보 중 Offset 에 대해서.

ITWeb/검색일반 2016.03.30 17:33

아는 것도 이제는 기억이 가물가물 합니다. 그래서 또 기록해 봅니다.

사내 교육을 하면서 lucene 기본 이론 교육을 하다, start offset 과 end offset 에 대해서 설명을 해주고 있었는데요.

end offset 이 실제 text의 offset 값 보다 1 크다는 것에 대한 질문이 있었습니다.


아는 건데 일단 가볍게라도 설명하고 넘어 가야해서 아무래도 highlight 기능을 위해서 그렇게 설정 하는것 같다고 하고 오늘 문서랑 소스 코드 좀 다시 살펴 봤습니다.


lucene in aciton 에서 퍼온 글)

The start offset is the character position in the original text where the token text begins, and the end offset is the position just after the last character of the token text.


end offset 이 실제 보다 1 큰 이유는 문서에 있습니다.

그런데 왜 이렇게 되었을까를 고민해 보면 내부 처리 방식을  확인해 봐야 합니다.


highlight 기능이기 때문에 이 작업에 필요한 class 파일과 fragment에 대한 처리 로직을 확인 하면 됩니다.

protected String makeFragment( StringBuilder buffer, int[] index, Field[] values, WeightedFragInfo fragInfo,
String[] preTags, String[] postTags, Encoder encoder ){
StringBuilder fragment = new StringBuilder();
final int s = fragInfo.getStartOffset();
int[] modifiedStartOffset = { s };
String src = getFragmentSourceMSO( buffer, index, values, s, fragInfo.getEndOffset(), modifiedStartOffset );
int srcIndex = 0;
for( SubInfo subInfo : fragInfo.getSubInfos() ){
for( Toffs to : subInfo.getTermsOffsets() ){
fragment
.append( encoder.encodeText( src.substring( srcIndex, to.getStartOffset() - modifiedStartOffset[0] ) ) )
.append( getPreTag( preTags, subInfo.getSeqnum() ) )
.append( encoder.encodeText( src.substring( to.getStartOffset() - modifiedStartOffset[0],
to.getEndOffset() - modifiedStartOffset[0] ) ) )
.append( getPostTag( postTags, subInfo.getSeqnum() ) );
srcIndex = to.getEndOffset() - modifiedStartOffset[0];
}
}
fragment.append( encoder.encodeText( src.substring( srcIndex ) ) );
return fragment.toString();
}

코드 보시면 아시겠죠.

기본적으로 String.substring( inclusive begin index, exclusive end index) 을 이용하기 때문에 end offset 값은 1 커야 하는 것입니다.

다른 의미로 보면 그냥 offset 정보와 text 의 length 정보를 한꺼번에 offsets 로 해결하기 좋은 방법으로 봐도 될 것 같습니다.


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

[Maven] maven 프로젝트 naming convention - group id, artifact id, version ...

ITWeb/검색일반 2016.02.29 16:32

이것도 매번 기억 못해서 찾아 보던 거라 그냥 기록해 봅니다.

기록이라기 보다는 그냥 스크랩이 맞는 것 같내요.


원본 링크)


원본)

  • groupId will identify your project uniquely across all projects, so we need to enforce a naming schema. It has to follow the package name rules, what means that has to be at least as a domain name you control, and you can create as many subgroups as you want. Look at More information about package names.

    eg. org.apache.maven, org.apache.commons

    A good way to determine the granularity of the groupId is to use the project structure. That is, if the current project is a multiple module project, it should append a new identifier to the parent's groupId.

    eg. org.apache.maven, org.apache.maven.plugins, org.apache.maven.reporting

  • artifactId is the name of the jar without version. If you created it then you can choose whatever name you want with lowercase letters and no strange symbols. If it's a third party jar you have to take the name of the jar as it's distributed.

    eg. maven, commons-math

  • version if you distribute it then you can choose any typical version with numbers and dots (1.0, 1.1, 1.0.1, ...). Don't use dates as they are usually associated with SNAPSHOT (nightly) builds. If it's a third party artifact, you have to use their version number whatever it is, and as strange as it can look.

    eg. 2.0, 2.0.1, 1.3.1


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

티스토리 툴바