Doğal Dil İşleme Uygulaması Nasıl Oluşturulur

Yayınlanan: 2022-03-11

Yazılım uygulamalarının insan dilini işlemesine izin veren bir teknoloji olan doğal dil işleme, son birkaç yılda oldukça yaygın hale geldi.

Google arama, kulağa doğal gelen soruları yanıtlama konusunda giderek daha yetenekli hale geliyor, Apple'ın Siri'si çok çeşitli soruları anlayabiliyor ve giderek daha fazla şirket, müşterilerle iletişim kurmak için (makul bir şekilde) akıllı sohbet ve telefon botlarını kullanıyor. Ancak bu görünüşte “akıllı” yazılım gerçekten nasıl çalışıyor?

Bu yazıda, bu uygulamaları harekete geçiren teknolojiyi öğrenecek ve kendi doğal dil işleme yazılımını nasıl geliştireceğinizi öğreneceksiniz.

Makale, bir haber alaka analizcisi oluşturma örnek sürecinde size yol gösterecektir. Bir hisse senedi portföyünüz olduğunu ve bir uygulamanın popüler haber web sitelerinde otomatik olarak gezinmesini ve portföyünüzle alakalı makaleleri tanımlamasını istediğinizi düşünün. Örneğin, hisse senedi portföyünüz Microsoft, BlackStone ve Luxottica gibi şirketleri içeriyorsa, bu üç şirketten bahseden makaleleri görmek istersiniz.

Stanford NLP Kitaplığına Başlarken

Doğal dil işleme uygulamaları, diğer makine öğrenimi uygulamaları gibi, birlikte çalışan bir dizi nispeten küçük, basit, sezgisel algoritma üzerine kuruludur. Tüm bu algoritmaların zaten uygulandığı ve entegre edildiği harici bir kitaplık kullanmak genellikle mantıklıdır.

Örneğimiz için, birçok dili destekleyen güçlü bir Java tabanlı doğal dil işleme kitaplığı olan Stanford NLP kitaplığını kullanacağız.

Bu kitaplıktan ilgilendiğimiz belirli bir algoritma, konuşma bölümü (POS) etiketleyicisidir. Bir metin parçasındaki her kelimeye konuşmanın bölümlerini otomatik olarak atamak için bir POS etiketleyici kullanılır. Bu POS etiketleyici, metindeki kelimeleri sözlük özelliklerine göre sınıflandırır ve çevrelerindeki diğer kelimelere göre analiz eder.

POS etiketleyici algoritmasının tam mekaniği bu makalenin kapsamı dışındadır, ancak bununla ilgili daha fazla bilgiyi buradan edinebilirsiniz.

Başlamak için yeni bir Java projesi oluşturacağız (en sevdiğiniz IDE'yi kullanabilirsiniz) ve Stanford NLP kitaplığını bağımlılıklar listesine ekleyeceğiz. Maven kullanıyorsanız, onu pom.xml dosyanıza eklemeniz yeterlidir:

 <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>

Uygulamanın bir makalenin içeriğini bir web sayfasından otomatik olarak ayıklaması gerekeceğinden, aşağıdaki iki bağımlılığı da belirtmeniz gerekecektir:

 <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>

Bu bağımlılıklar eklendiğinde, ilerlemeye hazırsınız.

Kazıma ve Temizleme Ürünleri

Analizörümüzün ilk bölümü, makalelerin alınmasını ve içeriklerinin web sayfalarından çıkarılmasını içerecektir.

Haber kaynaklarından makaleler alınırken, sayfalar genellikle makalenin kendisiyle ilgisi olmayan yabancı bilgilerle (gömülü videolar, giden bağlantılar, videolar, reklamlar vb.) delik deşik olur. Boilerpipe burada devreye giriyor.

Boilerpipe, ortalama bir cümlenin uzunluğu, içerik bloklarında kullanılan etiket türleri ve bağlantıların yoğunluğu gibi özellikleri kullanarak farklı içerik bloklarını analiz ederek bir haber makalesinin ana içeriğini tanımlayan “yığılmayı” ortadan kaldırmak için son derece sağlam ve verimli bir algoritmadır. Kazan borusu algoritmasının, makine görüşüne dayalı olanlar gibi hesaplama açısından çok daha pahalı diğer algoritmalarla rekabet ettiği kanıtlanmıştır. Proje sitesinde daha fazlasını öğrenebilirsiniz.

Boilerpipe kitaplığı, web sayfalarını kazımak için yerleşik desteğe sahiptir. Web'den HTML'yi getirebilir, HTML'den metin çıkarabilir ve çıkarılan metni temizleyebilir. Bir URL alacak ve bu görev için ArticleExtractor kullanarak en alakalı metni bir dize olarak döndürmek için Boilerpipe kullanacak bir işlev, extractFromURL tanımlayabilirsiniz:

 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 kitaplığı, makale çıkarıcının HTML biçimli haber makaleleri için özel olarak optimize edilmiş olmasıyla birlikte, kazan borusu algoritmasına dayalı farklı çıkarıcılar sağlar. ArticleExtractor , özellikle her içerik bloğunda ve giden bağlantı yoğunluğunda kullanılan HTML etiketlerine odaklanır. Bu, görevimize daha hızlı ama daha basit olan DefaultExtractor daha uygundur.

Yerleşik işlevler bizim için her şeyi halleder:

  • HTMLFetcher.fetch , HTML belgesini alır
  • getTextDocument metin belgesini çıkarır
  • CommonExtractors.ARTICLE_EXTRACTOR.getText , kazan borusu algoritmasını kullanarak ilgili metni makaleden çıkarır

Şimdi burada bulabileceğiniz optik devler Essilor ve Luxottica'nın birleşmeleriyle ilgili örnek bir makale ile deneyebilirsiniz. Bu URL'yi işleve besleyebilir ve ne çıktığını görebilirsiniz.

Ana işlevinize aşağıdaki kodu ekleyin:

 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); } }

Çıktınızı makalenin ana gövdesinde, reklamlar, HTML etiketleri ve giden bağlantılar olmadan görmelisiniz. Bunu çalıştırdığımda aldığımdan başlangıç ​​​​snippet'i:

 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...

Ve bu gerçekten de makalenin ana makale gövdesidir. Bunun uygulanmasının çok daha basit olduğunu hayal etmek zor.

Konuşma Bölümlerini Etiketleme

Artık ana makale gövdesini başarıyla çıkardığınıza göre, makalenin kullanıcının ilgisini çeken şirketlerden bahsedip bahsetmediğini belirlemeye çalışabilirsiniz.

Basitçe bir dize veya normal ifade araması yapmak isteyebilirsiniz, ancak bu yaklaşımın birkaç dezavantajı vardır.

Her şeyden önce, bir dizi araması yanlış pozitiflere eğilimli olabilir. Örneğin, Microsoft Excel'den bahseden bir makale, Microsoft'tan bahsedilmiş olarak etiketlenebilir.

İkinci olarak, normal ifadenin yapısına bağlı olarak, bir normal ifade araması yanlış negatiflere yol açabilir. Örneğin, "Luxottica'nın üç aylık kazançları beklentileri aştı" ifadesini içeren bir makale, beyaz boşluklarla çevrili "Luxottica" için arama yapan bir normal ifade aramasında gözden kaçabilir.

Son olarak, çok sayıda şirketle ilgileniyorsanız ve çok sayıda makaleyi işliyorsanız, kullanıcının portföyündeki her şirket için metnin tüm gövdesini aramak son derece zaman alıcı olabilir ve kabul edilemez performansa neden olabilir.

Stanford'un CoreNLP kitaplığı birçok güçlü özelliğe sahiptir ve bu üç sorunu da çözmenin bir yolunu sunar.

Analizörümüz için Parts-of-Speech (POS) etiketleyicisini kullanacağız. Özellikle, makaledeki tüm uygun isimleri bulmak ve bunları ilginç hisse senetleri portföyümüzle karşılaştırmak için POS etiketleyiciyi kullanabiliriz.

NLP teknolojisini dahil ederek, yalnızca etiketleyicimizin doğruluğunu iyileştirmekle ve yukarıda bahsedilen yanlış pozitif ve negatifleri en aza indirmekle kalmıyor, aynı zamanda özel isimler yalnızca küçük bir alt kümeyi içerdiğinden, hisse senedi portföyümüzle karşılaştırmamız gereken metin miktarını önemli ölçüde en aza indiriyoruz. makalenin tam metninden.

Portföyümüzü, üyelik sorgulama maliyeti düşük bir veri yapısında önceden işleyerek, bir makaleyi analiz etmek için gereken süreyi önemli ölçüde azaltabiliriz.

Stanford CoreNLP, yalnızca birkaç kod satırında POS Etiketleme sağlayabilen MaxentTagger adlı çok kullanışlı bir etiketleyici sağlar.

İşte basit bir uygulama:

 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); }

Etiketleme işlevi, tagPos , girdi olarak bir dize alır ve orijinal dizedeki sözcükleri ve konuşmanın karşılık gelen bölümünü içeren bir dize çıkarır. Ana işlevinizde, bir PortfolioNewsAnalyzer örneğini oluşturun ve kazıyıcının çıktısını etiketleyici işlevine besleyin; şöyle bir şey görmelisiniz:

 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...

Etiketli Çıktıyı Bir Küme Halinde İşleme

Şimdiye kadar bir haber makalesini indirmek, temizlemek ve etiketlemek için işlevler oluşturduk. Ancak yine de, makalenin kullanıcının ilgisini çeken şirketlerden herhangi birinden bahsedip bahsetmediğini belirlememiz gerekiyor.

Bunu yapmak için, tüm özel isimleri toplamamız ve portföyümüzdeki hisse senetlerinin bu özel isimlere dahil olup olmadığını kontrol etmemiz gerekiyor.

Tüm uygun isimleri bulmak için, önce etiketli dizge çıktısını belirteçlere bölmek (sınırlayıcı olarak boşluklar kullanarak), ardından alt çizgideki ( _ ) belirteçlerin her birini bölmek ve konuşma bölümünün uygun bir isim olup olmadığını kontrol etmek isteyeceğiz. .

Tüm uygun isimlere sahip olduğumuzda, onları amacımız için daha iyi optimize edilmiş bir veri yapısında saklamak isteyeceğiz. Örneğimiz için bir HashSet kullanacağız. HashSet , mükerrer girişlere izin vermemek ve girişlerin sırasını takip etmemek karşılığında çok hızlı üyelik sorgularına izin verir. Yalnızca üyelik sorgulamakla ilgilendiğimiz için HashSet , amaçlarımız için mükemmeldir.

Aşağıda özel isimlerin bölünmesini ve saklanmasını gerçekleştiren fonksiyon bulunmaktadır. Bu işlevi PortfolioNewsAnalyzer sınıfınıza yerleştirin:

 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; }

Ancak bu uygulamada bir sorun var. Bir şirketin adı birden fazla kelimeden oluşuyorsa (örneğin, Luxottica örneğinde Carl Zeiss), bu uygulama onu yakalayamayacaktır. Carl Zeiss örneğinde, "Carl" ve "Zeiss", sete ayrı ayrı eklenecek ve bu nedenle asla tek "Carl Zeiss" dizesini içermeyecektir.

Bu sorunu çözmek için ardışık tüm özel isimleri toplayabilir ve bunları boşluklarla birleştirebiliriz. İşte bunu başaran güncellenmiş uygulama:

 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; }

Şimdi işlev, bireysel özel isimler ve ardışık özel isimler (yani, boşluklarla birleştirilmiş) içeren bir küme döndürmelidir. propNounSet yazdırırsanız, aşağıdakine benzer bir şey görmelisiniz:

 [... 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, ...]

Portföyün PropNouns Seti ile Karşılaştırılması

Neredeyse bitirdik!

Önceki bölümlerde, bir makalenin gövdesini indirip çıkarabilen bir kazıyıcı, makale gövdesini ayrıştırabilen ve uygun isimleri tanımlayabilen bir etiketleyici ve etiketlenmiş çıktıyı alan ve uygun isimleri bir HashSet toplayan bir işlemci oluşturduk. Şimdi geriye kalan tek şey HashSet alıp ilgilendiğimiz şirketlerin listesiyle karşılaştırmak.

Uygulaması çok basittir. PortfolioNewsAnalyzer sınıfınıza aşağıdaki kodu ekleyin:

 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); }

Hepsini bir araya koy

Artık tüm uygulamayı çalıştırabiliriz - kazıma, temizleme, etiketleme, toplama ve karşılaştırma. İşte tüm uygulama boyunca çalışan fonksiyon. Bu işlevi PortfolioNewsAnalyzer sınıfınıza ekleyin:

 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); }

Sonunda uygulamayı kullanabiliriz!

Portföy şirketi olarak yukarıdakiyle aynı makaleyi ve Luxottica'yı kullanan bir örnek:

 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"); } }

Bunu çalıştırın ve uygulama "Makale portföy şirketlerinden bahsediyor" yazmalıdır.

Portföy şirketini Luxottica'dan makalede bahsedilmeyen bir şirkete ("Microsoft" gibi) değiştirin ve uygulama "Makale portföy şirketlerinden bahsetmiyor" yazmalıdır.

Bir NLP Uygulaması Oluşturmanın Zor Olması Gerekmiyor

Bu makalede, bir URL'den makale indiren, Boilerpipe kullanarak temizleyen, Stanford NLP kullanarak işleyen ve makalenin belirli ilgi referansları olup olmadığını kontrol eden bir uygulama oluşturma sürecini adım adım anlattık (bizim durumumuzda, bizim durumumuzdaki şirketler). portföy). Gösterildiği gibi, bu teknoloji dizisinden yararlanmak, aksi takdirde göz korkutucu bir görevi nispeten basit bir göreve dönüştürür.

Umarım bu makale sizi doğal dil işlemede faydalı kavram ve tekniklerle tanıştırmış ve kendi doğal dil uygulamalarını yazmanız için size ilham vermiştir.

[Not: Bu makalede atıfta bulunulan kodun bir kopyasını burada bulabilirsiniz.]