자연어 처리 앱을 구축하는 방법

게시 됨: 2022-03-11

소프트웨어 응용 프로그램이 인간의 언어를 처리할 수 있도록 하는 기술인 자연어 처리는 지난 몇 년 동안 상당히 보편화되었습니다.

Google 검색은 점점 더 자연스럽게 들리는 질문에 답할 수 있고 Apple의 Siri는 다양한 질문을 이해할 수 있으며 점점 더 많은 기업이 (합리적으로) 지능형 채팅 및 전화 봇을 사용하여 고객과 소통하고 있습니다. 그러나 이 겉보기에 "똑똑한" 소프트웨어가 실제로 어떻게 작동합니까?

이 기사에서는 이러한 응용 프로그램을 작동시키는 기술에 대해 배우고 자연어 처리 소프트웨어를 직접 개발하는 방법을 배웁니다.

이 기사에서는 뉴스 관련성 분석기를 구축하는 예제 프로세스를 안내합니다. 주식 포트폴리오가 있고 앱이 인기 있는 뉴스 웹사이트를 자동으로 크롤링하고 포트폴리오와 관련된 기사를 식별하기를 원한다고 상상해 보십시오. 예를 들어, 주식 포트폴리오에 Microsoft, BlackStone 및 Luxottica와 같은 회사가 포함되어 있는 경우 이 세 회사를 언급하는 기사를 보고 싶을 것입니다.

Stanford NLP 라이브러리 시작하기

다른 기계 학습 앱과 마찬가지로 자연어 처리 앱은 함께 작동하는 비교적 작고 단순하며 직관적인 여러 알고리즘을 기반으로 합니다. 이러한 모든 알고리즘이 이미 구현 및 통합되어 있는 외부 라이브러리를 사용하는 것이 종종 합리적입니다.

이 예에서는 다양한 언어를 지원하는 강력한 Java 기반 자연어 처리 라이브러리인 Stanford NLP 라이브러리를 사용합니다.

우리가 관심을 갖고 있는 이 라이브러리의 특정 알고리즘 중 하나는 품사(POS) 태거입니다. POS 태거는 텍스트의 모든 단어에 품사를 자동으로 할당하는 데 사용됩니다. 이 POS 태거는 어휘 특성을 기반으로 텍스트의 단어를 분류하고 주변의 다른 단어와 관련하여 분석합니다.

POS 태거 알고리즘의 정확한 역학은 이 기사의 범위를 벗어나지만 여기에서 자세히 알아볼 수 있습니다.

시작하려면 새 Java 프로젝트를 만들고(좋아하는 IDE를 사용할 수 있음) Stanford NLP 라이브러리를 종속성 목록에 추가합니다. Maven을 사용하는 경우 pom.xml 파일에 추가하기만 하면 됩니다.

 <dependency> <groupId>edu.stanford.nlp</groupId> <artifactId>stanford-corenlp</artifactId> <version>3.6.0</version> </dependency> <dependency> <groupId>edu.stanford.nlp</groupId> <artifactId>stanford-corenlp</artifactId> <version>3.6.0</version> <classifier>models</classifier> </dependency>

앱은 웹 페이지에서 기사의 콘텐츠를 자동으로 추출해야 하므로 다음 두 가지 종속성도 지정해야 합니다.

 <dependency> <groupId>de.l3s.boilerpipe</groupId> <artifactId>boilerpipe</artifactId> <version>1.1.0</version> </dependency>
 <dependency> <groupId>net.sourceforge.nekohtml</groupId> <artifactId>nekohtml</artifactId> <version>1.9.22</version> </dependency>

이러한 종속성이 추가되면 앞으로 나아갈 준비가 된 것입니다.

스크래핑 및 청소 용품

분석기의 첫 번째 부분은 기사를 검색하고 웹 페이지에서 내용을 추출하는 것입니다.

뉴스 소스에서 기사를 검색할 때 페이지는 일반적으로 기사 자체와 관련이 없는 관련 없는 정보(삽입된 비디오, 아웃바운드 링크, 비디오, 광고 등)로 가득 차 있습니다. 이것은 보일러 파이프가 작동하는 곳입니다.

Boilerpipe는 평균 문장의 길이, 콘텐츠 블록에 사용되는 태그 유형 및 링크 밀도와 같은 기능을 사용하여 다양한 콘텐츠 블록을 분석하여 뉴스 기사의 주요 콘텐츠를 식별하는 "클러터"를 제거하는 매우 강력하고 효율적인 알고리즘입니다. 보일러 파이프 알고리즘은 머신 비전 기반 알고리즘과 같이 훨씬 더 계산 비용이 많이 드는 다른 알고리즘과 경쟁력이 있는 것으로 입증되었습니다. 프로젝트 사이트에서 자세히 알아볼 수 있습니다.

Boilerpipe 라이브러리에는 웹 페이지 스크래핑에 대한 지원이 내장되어 있습니다. 웹에서 HTML을 가져오고 HTML에서 텍스트를 추출하고 추출된 텍스트를 정리할 수 있습니다. URL을 사용하고 이 작업에 ArticleExtractor 를 사용하여 가장 관련성이 높은 텍스트를 문자열로 반환하기 위해 extractFromURL 를 사용하는 extractFromURL 함수를 정의할 수 있습니다.

 import java.net.URL; import de.l3s.boilerpipe.document.TextDocument; import de.l3s.boilerpipe.extractors.CommonExtractors; import de.l3s.boilerpipe.sax.BoilerpipeSAXInput; import de.l3s.boilerpipe.sax.HTMLDocument; import de.l3s.boilerpipe.sax.HTMLFetcher; public class BoilerPipeExtractor { public static String extractFromUrl(String userUrl) throws java.io.IOException, org.xml.sax.SAXException, de.l3s.boilerpipe.BoilerpipeProcessingException { final HTMLDocument htmlDoc = HTMLFetcher.fetch(new URL(userUrl)); final TextDocument doc = new BoilerpipeSAXInput(htmlDoc.toInputSource()).getTextDocument(); return CommonExtractors.ARTICLE_EXTRACTOR.getText(doc); } }

보일러파이프 라이브러리는 보일러파이프 알고리즘을 기반으로 하는 다양한 추출기를 제공하며 ArticleExtractor 는 HTML 형식의 뉴스 기사에 특히 최적화되어 있습니다. ArticleExtractor 는 특히 각 콘텐츠 블록 및 아웃바운드 링크 밀도에 사용되는 HTML 태그에 중점을 둡니다. 이것은 더 빠르지만 더 간단한 DefaultExtractor 보다 우리 작업에 더 적합합니다.

내장 함수는 우리를 위해 모든 것을 처리합니다:

  • HTMLFetcher.fetch 는 HTML 문서를 가져옵니다.
  • getTextDocument 는 텍스트 문서를 추출합니다.
  • CommonExtractors.ARTICLE_EXTRACTOR.getText 는 보일러 파이프 알고리즘을 사용하여 기사에서 관련 텍스트를 추출합니다.

이제 여기에서 찾을 수 있는 광학 거대 기업인 Essilor와 Luxottica의 합병에 관한 예제 기사를 통해 이를 시험해 볼 수 있습니다. 이 URL을 함수에 제공하고 결과를 볼 수 있습니다.

기본 함수에 다음 코드를 추가합니다.

 public class App { public static void main( String[] args ) throws java.io.IOException, org.xml.sax.SAXException, de.l3s.boilerpipe.BoilerpipeProcessingException { String urlString = "http://www.reuters.com/article/us-essilor-ma-luxottica-group-idUSKBN14Z110"; String text = BoilerPipeExtractor.extractFromUrl(urlString); System.out.println(text); } }

광고, HTML 태그 및 아웃바운드 링크 없이 기사 본문의 출력에 표시되어야 합니다. 다음은 내가 이것을 실행할 때 얻은 것의 시작 스니펫입니다.

 MILAN/PARIS Italy's Luxottica (LUX.MI) and France's Essilor (ESSI.PA) have agreed a 46 billion euro ($49 billion) merger to create a global eyewear powerhouse with annual revenue of more than 15 billion euros. The all-share deal is one of Europe's largest cross-border tie-ups and brings together Luxottica, the world's top spectacles maker with brands such as Ray-Ban and Oakley, with leading lens manufacturer Essilor. "Finally ... two products which are naturally complementary -- namely frames and lenses -- will be designed, manufactured and distributed under the same roof," Luxottica's 81-year-old founder Leonardo Del Vecchio said in a statement on Monday. Shares in Luxottica were up by 8.6 percent at 53.80 euros by 1405 GMT (9:05 am ET), with Essilor up 12.2 percent at 114.60 euros. The merger between the top players in the 95 billion eyewear market is aimed at helping the businesses to take full advantage of expected strong demand for prescription spectacles and sunglasses due to an aging global population and increasing awareness about eye care. Jefferies analysts estimate that the market is growing at between...

그리고 그것은 실제로 기사의 주요 기사 본문입니다. 이것이 구현하기가 훨씬 더 간단하다는 것을 상상하기 어렵습니다.

품사 태그 지정

이제 주요 기사 본문을 성공적으로 추출했으므로 기사에 사용자가 관심을 갖고 있는 회사가 언급되어 있는지 확인하는 작업을 할 수 있습니다.

단순히 문자열이나 정규식 검색을 하고 싶을 수도 있지만 이 접근 방식에는 몇 가지 단점이 있습니다.

우선, 문자열 검색은 가양성(false positive)에 취약할 수 있습니다. 예를 들어 Microsoft Excel을 언급하는 기사는 Microsoft를 언급하는 것으로 태그가 지정될 수 있습니다.

둘째, 정규식의 구성에 따라 정규식 검색이 거짓 부정으로 이어질 수 있습니다. 예를 들어 "Luxottica의 분기별 수입이 기대치를 초과했습니다"라는 문구가 포함된 기사는 공백으로 둘러싸인 "Luxottica"를 검색하는 정규식 검색에서 놓칠 수 있습니다.

마지막으로, 많은 수의 회사에 관심이 있고 많은 기사를 처리하는 경우 사용자 포트폴리오에 있는 모든 회사에 대해 전체 텍스트를 검색하는 것은 시간이 많이 소요되어 허용할 수 없는 성능을 초래할 수 있습니다.

Stanford의 CoreNLP 라이브러리에는 많은 강력한 기능이 있으며 이러한 세 가지 문제를 모두 해결할 수 있는 방법을 제공합니다.

분석기의 경우 품사(POS) 태거를 사용합니다. 특히, POS 태거를 사용하여 기사에서 모든 고유 명사를 찾고 흥미로운 주식 포트폴리오와 비교할 수 있습니다.

NLP 기술을 통합함으로써 우리는 태거의 정확성을 향상시키고 위에서 언급한 오탐지 및 부정성을 최소화할 뿐만 아니라 고유명사는 작은 부분 집합으로 구성되기 때문에 주식 포트폴리오와 비교해야 하는 텍스트의 양을 극적으로 최소화합니다. 기사의 전체 텍스트 중.

회원 쿼리 비용이 낮은 데이터 구조로 포트폴리오를 사전 처리함으로써 기사 분석에 필요한 시간을 획기적으로 줄일 수 있습니다.

Stanford CoreNLP는 몇 줄의 코드로 POS 태깅을 제공할 수 있는 MaxentTagger라는 매우 편리한 태거를 제공합니다.

다음은 간단한 구현입니다.

 public class PortfolioNewsAnalyzer { private HashSet<String> portfolio; private static final String modelPath = "edu\\stanford\\nlp\\models\\pos-tagger\\english-left3words\\english-left3words-distsim.tagger"; private MaxentTagger tagger; public PortfolioNewsAnalyzer() { tagger = new MaxentTagger(modelPath); } public String tagPos(String input) { return tagger.tagString(input); }

태거 함수 tagPos 는 문자열을 입력으로 받아 해당 품사와 함께 원래 문자열의 단어를 포함하는 문자열을 출력합니다. 기본 기능에서 PortfolioNewsAnalyzer 를 인스턴스화하고 스크레이퍼의 출력을 태거 기능에 입력하면 다음과 같이 표시되어야 합니다.

 MILAN/PARIS_NN Italy_NNP 's_POS Luxottica_NNP -LRB-_-LRB- LUX.MI_NNP -RRB-_-RRB- and_CC France_NNP 's_POS Essilor_NNP -LRB-_-LRB- ESSI.PA_NNP -RRB-_-RRB- have_VBP agreed_VBN a_DT 46_CD billion_CD euro_NN -LRB-_-LRB- $_$ 49_CD billion_CD -RRB-_-RRB- merger_NN to_TO create_VB a_DT global_JJ eyewear_NN powerhouse_NN with_IN annual_JJ revenue_NN of_IN more_JJR than_IN 15_CD billion_CD euros_NNS ._. The_DT all-share_JJ deal_NN is_VBZ one_CD of_IN Europe_NNP 's_POS largest_JJS cross-border_JJ tie-ups_NNS and_CC brings_VBZ together_RB Luxottica_NNP ,_, the_DT world_NN 's_POS top_JJ spectacles_NNS maker_NN with_IN brands_NNS such_JJ as_IN Ray-Ban_NNP and_CC Oakley_NNP ,_, with_IN leading_VBG lens_NN manufacturer_NN Essilor_NNP ._. ``_`` Finally_RB ..._: two_CD products_NNS which_WDT are_VBP naturally_RB complementary_JJ --_: namely_RB frames_NNS and_CC lenses_NNS --_: will_MD be_VB designed_VBN ,_, manufactured_VBN and_CC distributed_VBN under_IN the_DT same_JJ roof_NN ,_, ''_'' Luxottica_NNP 's_POS 81-year-old_JJ founder_NN Leonardo_NNP Del_NNP Vecchio_NNP said_VBD in_IN a_DT statement_NN on_IN Monday_NNP ._. Shares_NNS in_IN Luxottica_NNP were_VBD up_RB by_IN 8.6_CD percent_NN at_IN 53.80_CD euros_NNS by_IN 1405_CD GMT_NNP -LRB-_-LRB- 9:05_CD am_NN ET_NNP -RRB-_-RRB- ,_, with_IN Essilor_NNP up_IN 12.2_CD percent_NN at_IN 114.60_CD euros_NNS ._. The_DT merger_NN between_IN the_DT top_JJ players_NNS in_IN the_DT 95_CD billion_CD eyewear_NN market_NN is_VBZ aimed_VBN at_IN helping_VBG the_DT businesses_NNS to_TO take_VB full_JJ advantage_NN of_IN expected_VBN strong_JJ demand_NN for_IN prescription_NN spectacles_NNS and_CC sunglasses_NNS due_JJ to_TO an_DT aging_NN global_JJ population_NN and_CC increasing_VBG awareness_NN about_IN...

태그가 지정된 출력을 세트로 처리

지금까지 뉴스 기사를 다운로드, 정리 및 태그 지정하는 기능을 구축했습니다. 그러나 기사에 사용자가 관심을 가질 만한 회사가 있는지 여부는 여전히 확인해야 합니다.

이를 위해서는 모든 고유명사를 수집하고 포트폴리오의 주식이 해당 고유명사에 포함되어 있는지 확인해야 합니다.

모든 고유 명사를 찾으려면 먼저 태그가 지정된 문자열 출력을 토큰으로 분할하고(공백을 구분 기호로 사용) 밑줄( _ )에서 각 토큰을 분할하고 품사가 고유 명사인지 확인합니다. .

고유 명사를 모두 확보한 후에는 목적에 더 잘 최적화된 데이터 구조에 해당 명사를 저장하고 싶을 것입니다. 이 예에서는 HashSet 을 사용합니다. 중복 항목을 허용하지 않고 항목 순서를 추적하지 않는 대가로 HashSet 은 매우 빠른 구성원 쿼리를 허용합니다. 우리는 멤버십 쿼리에만 관심이 있기 때문에 HashSet 이 우리의 목적에 완벽합니다.

다음은 고유명사 분리 및 저장을 구현하는 함수입니다. 이 함수를 PortfolioNewsAnalyzer 클래스에 배치합니다.

 public static HashSet<String> extractProperNouns(String taggedOutput) { HashSet<String> propNounSet = new HashSet<String>(); String[] split = taggedOutput.split(" "); for (String token: split ){ String[] splitTokens = token.split("_"); if(splitTokesn[1].equals("NNP")){ propNounSet.add(splitTokens[0]); } } return propNounSet; }

그러나 이 구현에는 문제가 있습니다. 회사 이름이 여러 단어로 구성된 경우(예: Luxottica의 예에서 Carl Zeiss) 이 구현은 이를 포착할 수 없습니다. Carl Zeiss의 예에서 "Carl"과 "Zeiss"는 세트에 별도로 삽입되므로 단일 문자열 "Carl Zeiss"를 포함하지 않습니다.

이 문제를 해결하기 위해 우리는 모든 연속 된 고유 명사를 수집하고 공백으로 결합할 수 있습니다. 다음은 이를 수행하는 업데이트된 구현입니다.

 public static HashSet<String> extractProperNouns(String taggedOutput) { HashSet<String> propNounSet = new HashSet<String>(); String[] split = taggedOutput.split(" "); List<String> propNounList = new ArrayList<String>(); for (String token: split ){ String[] splitTokens = token.split("_"); if(splitTokens[1].equals("NNP")){ propNounList.add(splitTokens[0]); } else { if (!propNounList.isEmpty()) { propNounSet.add(StringUtils.join(propNounList, " ")); propNounList.clear(); } } } if (!propNounList.isEmpty()) { propNounSet.add(StringUtils.join(propNounList, " ")); propNounList.clear(); } return propNounSet; }

이제 함수는 개별 고유 명사 연속 고유 명사(즉, 공백으로 연결됨)가 포함된 집합을 반환해야 합니다. propNounSet 를 인쇄하면 다음과 같은 내용이 표시되어야 합니다.

 [... Monday, Gianluca Semeraro, David Goodman, Delfin, North America, Luxottica, Latin America, Rossi/File Photo, Rome, Safilo Group, SFLG.MI, Friday, Valentina Za, Del Vecchio, CEO Hubert Sagnieres, Oakley, Sagnieres, Jefferies, Ray Ban, ...]

PropNouns 세트에 대한 포트폴리오 비교

거의 완료되었습니다!

이전 섹션에서 기사 본문을 다운로드하고 추출할 수 있는 스크레이퍼, 기사 본문을 구문 분석하고 고유명사를 식별할 수 있는 태거, 태그가 지정된 출력을 가져와 고유명사를 HashSet 로 수집하는 프로세서를 구축했습니다. 이제 남은 일은 HashSet 을 가져와 우리가 관심 있는 회사 목록과 비교하는 것입니다.

구현은 매우 간단합니다. PortfolioNewsAnalyzer 클래스에 다음 코드를 추가합니다.

 private HashSet<String> portfolio; public PortfolioNewsAnalyzer() { portfolio = new HashSet<String>(); } public void addPortfolioCompany(String company) { portfolio.add(company); } public boolean arePortfolioCompaniesMentioned(HashSet<String> articleProperNouns){ return !Collections.disjoint(articleProperNouns, portfolio); }

함께 모아서

이제 스크래핑, 청소, 태그 지정, 수집 및 비교와 같은 전체 응용 프로그램을 실행할 수 있습니다. 다음은 전체 애플리케이션에서 실행되는 기능입니다. PortfolioNewsAnalyzer 클래스에 다음 함수를 추가합니다.

 public boolean analyzeArticle(String urlString) throws IOException, SAXException, BoilerpipeProcessingException { String articleText = extractFromUrl(urlString); String tagged = tagPos(articleText); HashSet<String> properNounsSet = extractProperNouns(tagged); return arePortfolioCompaniesMentioned(properNounsSet); }

마지막으로 앱을 사용할 수 있습니다!

다음은 위와 동일한 기사를 사용하고 포트폴리오 회사로 Luxottica를 사용한 예입니다.

 public static void main( String[] args ) throws IOException, SAXException, BoilerpipeProcessingException { PortfolioNewsAnalyzer analyzer = new PortfolioNewsAnalyzer(); analyzer.addPortfolioCompany("Luxottica"); boolean mentioned = analyzer.analyzeArticle("http://www.reuters.com/article/us-essilor-ma-luxottica-group-idUSKBN14Z110"); if (mentioned) { System.out.println("Article mentions portfolio companies"); } else { System.out.println("Article does not mention portfolio companies"); } }

이것을 실행하면 앱에서 "Article은 포트폴리오 회사를 언급합니다."를 인쇄해야 합니다.

포트폴리오 회사를 Luxottica에서 기사에 언급되지 않은 회사(예: "Microsoft")로 변경하면 앱에서 "기사에 포트폴리오 회사가 언급되지 않음"이 인쇄되어야 합니다.

NLP 앱을 구축하는 것은 어려울 필요가 없습니다

이 기사에서는 URL에서 기사를 다운로드하고, Boilerpipe를 사용하여 정리하고, Stanford NLP를 사용하여 처리하고, 기사에서 관심 있는 특정 참조를 만드는지 확인하는 애플리케이션을 구축하는 과정을 단계별로 살펴보았습니다. 포트폴리오). 입증된 바와 같이, 이러한 일련의 기술을 활용하면 어려운 작업이 비교적 간단한 작업으로 바뀝니다.

이 기사가 자연어 처리의 유용한 개념과 기술을 소개하고 자연어 응용 프로그램을 직접 작성하는 데 영감을 주기를 바랍니다.

[참고: 이 기사에서 참조한 코드 사본은 여기에서 찾을 수 있습니다.]