Przetwarzanie języka naturalnego w JavaScript z użyciem biblioteki Natural

Kurs_wykorzystanie_AI_ikonaPoniższy artykuł to jedna z części tworzonego na bieżąco kursu, który pomoże Ci opanować praktyczne umiejętności, które pozwolą Ci w pełni wykorzystać potencjał sztucznej inteligencji. Chcesz przejść do pozostałych lekcji?

Znajdziesz je tutaj: Praktyczne wykorzystanie AI

Przy zadaniach związanych z przetwarzaniem języka naturalnego oczywistym wyborem staje się wybór popularnych bibliotek przeznaczonych dla języka Python. Istnieją jednak alternatywy, pozwalające na zbudowanie takich funkcjonalności w innych językach, takich jak JavaScript. Zadania związane z przetwarzaniem języka naturalnego (ang. Natural Language Processing, NLP) opierają się na parsowaniu, analizie oraz rekonstrukcji języka zarówno pisanego jak i mówionego.

Sztandarowym przykładem zastosowania przetwarzania języka naturalnego jest wykorzystanie asystentów głosowych. Możliwość komunikacji z naszym urządzeniem poprzez mowę (czy tekst) jest możliwe dzięki zastosowaniu wysublimowanych technik przetwarzania oraz analizy danych oraz uczenia maszynowego (w tym przypadku bardzo rozbudowanych sieci neuronowych).

Mimo iż uczenie maszynowe oraz NLP nie jest tym samym, to bardzo często oba te zagadnienia łączą się ze sobą, umożliwiając nam wykorzystywanie zaawansowanych technologicznie rozwiązań.

Asystent głosowy
Asystent głosowy to przykład zastosowania technik przetwarzania języka naturalnego.

Wśród zadań stojących w obszarze zainteresowań dziedziny NLP, możemy wyróżnić wiele procedur wykonujących przetwarzanie tekstu takich jak tokenizacja, lematyzacja czy stemowanie, umożliwiających dalszą analizę danych tekstowych.

Dane takie możemy wykorzystać do przeprowadzenia analizy syntaktycznej (bazującej na budowie słów, oraz ich ilości) czy analizy semantycznej (kontekstowej, znaczeniowej). Techniki takie prowadzą do badania analizy wydźwięku, czy dokonywania klasyfikacji tekstów.

W poniższym artykule przyjrzymy się wybranym zagadnieniom związanym z przetwarzaniem języka naturalnego w języku JavaScript z wykorzystaniem otwartoźródłowej biblioteki Natural.

Przygotowanie projektu

Pierwszym krokiem będzie stworzenie projektu, który umożliwi przedstawienie działania wyżej wymienionych technik.

  • W wybranym przez nas katalogu utwórzmy nowy katalog z projektem, wykonując polecenia:
mkdir nlpprocessing
cd nlpprocessing
  • Następnie przygotujmy nowy projekt wykonując polecenia:
npm init
touch index.js
  • Zainstalujmy również bibliotekę Natural poleceniem:
npm install natural

Tak przygotowany projekt możemy wykorzystać do przedstawienia funkcjonalności biblioteki Natural.

Tokenizacja

Podstawowym zadaniem przy przetwarzaniu języka naturalnego jest podział tekstu na odpowiednio małe jednostki znaczeniowe, które w zależności od przypadku mogą być słowem, zdaniem lub inną jednostką (na przykład, słowa mogą być podzielone na jeszcze mniejsze części, takie jak sylaby, przyrostki czy przedrostki).

Każda taka jednostka, czyli tzw. token posiada własne znaczenie. Możemy więc zauważyć, że tokenizacja to proces, który dzieli tekst na mniejsze elementy, bardziej ukierunkowane pod względem znaczenia. Proces tokenizacji ma ogromne znaczenie przy dalszym przetwarzaniu języka, np. w algorytmach uczenia maszynowego. Odpowiednio przeprowadzana tokenizacja ma wpływ na wydajność oraz wyniki algorytmu. 

Tekst możemy podzielić na wiele sposobów. Wsród najprostszych jest dzielenie tekstów w oparciu o znaki odstępu. Wynikiem takiego procesu jest więc zbiór słów. Przy takim procesie często przeprowadzane są dodatkowe zadania, np. zmiana wielkości liter, usunięcie znaków odstępowych oraz interpunkcyjnych czy formatowania.

Często korzystnym zabiegiem z perspektywy dalszej analizy, jest usunięcie słów z tzw. stop-listy, czyli zbioru słów najpopularniejszych dla danego języka (na przykład słowa the, this w języku angielskim), które nie mają istotnego znaczenia dla dalszych procesów.

Przyjrzyjmy się, w jaki sposób wykonać taki proces z użyciem biblioteki Natural. Do tego celu wykorzystamy metodę tokenie obiektu WordTokenizer.

  • Przyjmijmy, że chcemy podzielić na tokeny następujący tekst:
const data = "John Doe is sometimes used to refer to a typical male in other contexts as well, in a similar manner to John Q."
  • Najpierw zaimportujmy bibliotekę Natural, dodając kod:
const natural = require("natural");
  • Oraz stwórzmy nowy obiekt klasy WordTokenizer:

const tokenizer = new natural.WordTokenizer();
  • Aby przeprowadzić proces tokenizacji, wykonajmy metodę tokenize:

const tokenizedData = tokenizer.tokenize(data);
console.log(tokenizedData)
  • Wynikiem jest tablica tokenów:
[
  'John',      'Doe',      'is',
  'sometimes', 'used',     'to',
  'refer',     'to',       'a',
  'typical',   'male',     'in',
  'other',     'contexts', 'as',
  'well',      'in',       'a',
  'similar',   'manner',   'to',
  'John',      'Q'
]

Jak widać powyżej, z tekstu zostały usunięte znaki interpunkcyjne, a tekst został podzielony na słowa, w oparciu o znaki spacji. Zachowane zostały wielkie litery.

Tak przygotowany tekst z powodzeniem może zostać wykorzystany w dalszych krokach.

Stemowanie

Popularną techniką wykorzystywaną do normalizacji danych jest stemowanie (ang. stemming). Celem tego procesu jest wyeliminowanie pewnych różnic między słowami, a przez co znaczące zmniejszenie zbioru słów wystepujących w tekście.

Eliminacja tych różnic, polega na unikaniu form mnogich, końcówek fleksyjnych, przyrostków, form czasowników, itp. Po takim procesie wynikiem jest zbiór tzw. stemów, które nie muszą być poprawnymi słowami w kontekście języka. Proces ten nie powiem być wykorzystywany, gdy dalszej analizie, ważne są formy lub odmiany językowe, ze względu na to, że podczas procesu zachowywane jest tylko podstawowe znaczenie słowa.

  • Przeprowadzimy więc przykładowy proces stemowania na wcześniej stokenizowanego zdania:
const stems = tokenizedData.map(natural.PorterStemmer.stem)
console.log(stems)
  • Wynikiem tego procesu będzie tablica:
[
  'john',    'doe',     'is',
  'sometim', 'us',      'to',
  'refer',   'to',      'a',
  'typic',   'male',    'in',
  'other',   'context', 'as',
  'well',    'in',      'a',
  'similar', 'manner',  'to',
  'john',    'Q'
]

Badanie poprawności pisowni

Odległość Levensteina to miara służąca to oceny liczby operacji koniecznych do przekształcenia słowa w inne słowo. Wśród takich operacji możemy wyróżnić wstawianie, usuwanie oraz zamianę znaku. Dzięki takiej mierze możemy jasno zmierzyć różnicę między wybranymi słowami. Miara taka jest przydatna do oceny poprawności pisowni danego słowa oraz automatycznej korekcji błędów.

  • Przyjrzyjmy się, w jaki sposób obliczyć wartości takiej miary. Przyjmijmy, że chcemy ją obliczyć dla dwóch słów: typical oraz types:

const words = ["typical", "types"]
console.log(natural.LevenshteinDistance.apply(null, words))
Liczba operacji koniecznych do zmiany takiego słowa wynosi 4 (zamiana i -> e, c -> s oraz usunięcie liter a oraz l).
  • Posiadając korpus z poprawnymi słowami, możemy dokonywać poprawy pisowni oraz badać poprawność:
const corpus = ['something'];
const spellcheck = new natural.Spellcheck(corpus);
  • Zbadajmy poprawność takiego słowa:
const word = 'smthing';
console.log(spellcheck.isCorrect(word));
Wynikiem oczywiście będzie false.
  • Możemy również dokonać poprawy pisowni:
console.log(spellcheck.getCorrections('soemthing', 1));

Wynikiem będzie [ 'something’ ], czyli poprawne słowo.

Przy odpowiednio dużym korpusie, możemy to zrobić w sposób bardziej naturalny, uwzględniając bardziej realne przypadki.

Analiza sentymentu

Wśród zadań przy przetwarzaniu języka naturalnego możemy wyróżnić m.in. badanie semantyki tekstu, czyli jego znaczenia. Biblioteka Natural pozwala na badanie wydźwięku (pozytywnego lub negatywnego) dla wybranego tekstu. Możemy wykonać ten proces wykorzystując klasę SentimentAnalyzer.

Analiza sentymentu
Analiza sentymentu to proces analizowania i oceniania opinii wyrażanych w tekście w celu określenia, czy są one pozytywne, negatywne czy neutralne.
  • Zadeklarujmy najpierw naszą klasę przeprowadzającą analizę:
const Analyzer = natural.SentimentAnalyzer;
const stemmer = natural.PorterStemmer;
const analyzer = new Analyzer("English", stemmer, "afinn");
>Parametrami konstruktora klasy Analyzer, są tutaj język, obiekt stemmera oraz słownik (z predefiniowanej listy w bibliotece).
  • Aby przeprowadzić analizę, wykonajmy następujące polecenie:
console.log(analyzer.getSentiment(["I", "love", "dogs"]));
Wynikiem jest 0.66. Oznacza to, że z prawdopodobieństwem wynoszącym 2/3, zdanie „I love dogs” jest zdaniem posiadającym pozytywny wydźwięk.

Zgodnie z zasadami rachunku prawdopodobieństwa, możemy przyjąć, że poniżej wartości 0.5, zdanie interpretowalibyśmy jako posiadające negatywny wydźwięk.

Klasyfikacja tekstów

Zadaniem klasyfikacji binarnej jest podział wybranego zbioru na dwie klasy, odpowiednio oznaczając każdy dokument (nadając mu etykietę). Przyjmijmy więc, że mamy zbiór danych, w którym część dokumentów (w naszym przypadku zdań) jest informacją o kotach, a część o psach. Naszym zadaniem jest stworzenie klasyfikatora badającego, o jakim typie zwierzęcia jest mowa w zdaniu.

Biblioteka Natural posiada dwa typy klasyfikatorów: wykorzystujący regresję logistyczną oraz naiwny klasyfikator Bayesa.

Więcej o tych algorytmach możesz przeczytać:

  1. W artykule o regresji logistycznej.
  2. W artykule o naiwnym klasyfikatorze Bayesa.
  • Wykorzystajmy w naszym przykładzie ten drugi:
Const classifier = new natural.BayesClassifier();
  • Dodajmy zbór danych. Każdemu zdaniu nadajmy również odpowiednią etykietę:
classifier.addDocument('i love dogs', 'dog');
classifier.addDocument('dogs are the best friends', 'dog');
classifier.addDocument('i have a cat', 'cat');
classifier.addDocument('cats are amazing', 'cat');
  • Następnie musimy dokonać treningu naszego klasyfikatora:
classifier.train();
  • Sprawdźmy działanie, wprowadzając zdanie:
console.log(classifier.classify('my dog is fast'));

Wynikiem procesu klasyfikacji będzie etykieta „dog”.

Podsumowanie

Zagadnienia związane z przetwarzaniem języka naturalnego, bardzo często wymagają dużej ilości nauki, ze względu na liczne przypadki brzegowe związane z językiem naturalnym. Jednak stosowanie pewnych mechanizmów w postaci gotowych bibliotek, często umożliwia nam stworzenie kompletnych funkcjonalności bez posiadania rozbudowanej wiedzy o procesach związanych z technikami NLP.

Przykładem takiej biblioteki jest Natural dla języka JavaScript, która jest bogatym narzędziem do przeprowadzania procesów związanych z przetwarzaniem języka naturalnego.

  • Natural Documentation, http://naturalnode.github.io/natural/
  • Burak Kanber, Uczenie maszynowe z językiem Javascript, Helion, 2019
  • Aurélien Géron, Uczenie maszynowe z użyciem Scikit-Learn i TensorFlow, Helion 2021
Reguły asocjacyjne w JavaScript

Chcesz stworzyć projekt oparty na algorytmach uczenia maszynowego i sztucznej inteligencji? Szukasz doświadczonego zespołu specjalistów?

Sprawdź co mamy do zaoferowania!

Adam Gałęcki

Internet Marketing Specialist | Na co dzień tworzę content i prowadzę kampanie SEO.

Zostaw komentarz:

Witryna jest chroniona przez reCAPTCHA i Google Politykę Prywatności oraz obowiązują Warunki Korzystania z Usługi.