Site icon AppTractor

Вычисление семантического расстояния между словами с помощью Natural Language Framework

Такие фрагменты текста естественного языка, как слова и предложения, могут иметь семантическое сходство между собой. На этой концепции основано большинство подсказок, и она реализуется с помощью класса NLEmbedding фреймворка Natural Language.

Обработка естественного языка позволяет компьютерам понимать человеческий язык и взаимодействовать с ним. Она помогает обрабатывать и анализировать текстовые данные, извлекать значимую информацию и определять взаимосвязи между словами и фразами. Класс NLEmbedding обеспечивает возможность структурированного отображения и анализа этих связей.

Эта возможность крайне важна для понимания и обработки текста. На ней основан целый ряд функций в экосистеме Apple:

В этой справочной статье мы рассмотрим класс NLEmbedding, чтобы понять, как использовать его преимущества.

Вычисление семантического расстояния

Получив строку, NLEmbedding создает окружение, заполненное другими фрагментами текста, которые имеют семантическое сходство.

Некоторые из них будут расположены дальше от этой строки, а другие — ближе. Это расстояние регулируется семантическим сходством противопоставленных строк. Чем меньше расстояние, тем больше сходство. NLEmbedding представляет эту окружение — карту строк — в векторном пространстве, где строки — векторы.

import NaturalLanguage

func getSemanticDistance(for word: String, in language: NLLanguage) {
		
    // 1. Create the embedding for the language the word belongs to
    if let embedding = NLEmbedding.wordEmbedding(for: language) {
        
        // 2. Find the neighbors for the word
        embedding.enumerateNeighbors(for: word, maximumCount: 10) { neighbor, distance in
            
            // 3. Acces the neighbor distance from the word
            print("\(neighbor): \(distance)")
            
            return true
        }
    }
}

Чтобы приступить к реализации функций получения семантического расстояния с помощью NLEmbedding, начните с импорта фреймворка NaturalLanguage, а затем:

  1. Создайте экземпляр NLEmbedding и вызовите метод wordEmbedding(for:), передав в качестве параметра язык обрабатываемой строки. Если он доступен, то вернет объект NLEmbedding, в противном случае вернет nil.
  2. Вызовите метод enumerateNeighbors(for:maximumCount:distanceType:using:), передайте строку, соседи которой вам нужны, и максимальное количество раз, которое нужно вызвать метод. Для каждого найденного соседа будет возвращено его расстояние до обрабатываемого слова.

Объект NLEmbedding предоставляет доступ к нескольким полезным сведениям, таким как:

Класс NLEmbedding также предоставляет метод для получения расстояния между одним словом и другим.

func semanticDistanceBetween(firstWord: String, and secondWord: String, for language: NLLanguage) {
    // 1. Create the embedding for the language the words belongs to
    if let embedding = NLEmbedding.wordEmbedding(for: language) {
        // 2. Calculate the distance between words
        let distance = embedding.distance(between: firstWord, and: secondWord)
        
        print("The distance between \(firstWord) and \(secondWord) is \(distance).")
    }
}

Метод distance(between:and:distanceType:) вычисляет расстояние между двумя строками в векторном пространстве. В качестве параметров он принимает:

Пример с представлением SwiftUI

Ниже приведен пример представления SwiftUI для поиска синонимов слова с использованием фреймворка Natural Language.

import NaturalLanguage

struct SynonymsView: View {
    
    @State var text: String = ""
    @State var results: [String] = []
    @State var errorMessage: String? = nil
    
    var body: some View {
        
        List {
            Section {
                TextEditor(text: $text)
                    .frame(height: 100)
                    .textFieldStyle(.plain)
            }
            
            Section {
                Button("Find synonyms") {
                    self.findSynomys()
                }
            }
            
            if results.isEmpty {
                ContentUnavailableView(
                    errorMessage != nil ? errorMessage! : "Text wasn't analyzed",
                    systemImage: "doc.text.magnifyingglass"
                )
            } else {
                Section {
                    ForEach(results, id: \.self ) { item in
                        Text("\(item)")
                    }
                }
            }
        }
    }
    
    private func findSynomys() {
        do {
            try self.getSynonyms(word: text)
        } catch {
            errorMessage = "Error: \(error)"
        }
    }
    
    // Detecting the hypothethical languages the word belongs to
    private func getLanguages(of text: String) -> [Dictionary<NLLanguage,Double>.Element]? {
        let languageRecognizer = NLLanguageRecognizer()
        languageRecognizer.processString(text)
        
        let hypothesis = languageRecognizer
            .languageHypotheses(withMaximum: 5)
            .sorted(by: { $0.value > $1.value })
        
        return hypothesis
    }
    
    // Looking for synonyms
    private func getSynonyms(word: String) throws {
        
        self.results.removeAll()
        
        guard let languages = getLanguages(of: word) else {
            throw ErrorMessage.languageNotRecognized
        }
        
        for language in languages {
            if let embedding = NLEmbedding.wordEmbedding(for: language.key) {
                guard embedding.contains(word.lowercased()) else {
                    throw ErrorMessage.wordNotIncludedInEmbedding
                }
                
                embedding.enumerateNeighbors(for: word.lowercased(), maximumCount: 5) { neighbor, distance in
                    self.results.append(neighbor)
                    return true
                }
            }
        }
        
        guard (self.results.count > 0) else {
            throw ErrorMessage.noSynonymFound
        }
    }
    
    // Handling Errors
    enum ErrorMessage: Error, CustomStringConvertible {
        
        case noSynonymFound
        case wordNotIncludedInEmbedding
        case vocabularyNotAvailable
        case languageNotRecognized
        
        var description: String {
            switch self {
            case .noSynonymFound:
                "No synonym found"
            case .wordNotIncludedInEmbedding:
                "Word not included in the vocabulary"
            case .vocabularyNotAvailable:
                "Vocabulary not available"
            case .languageNotRecognized:
                "Language not recognized"
            }
        }
    }
    
}

В приведенном выше примере пользователь вводит слово, и при нажатии кнопки с помощью фреймворка Natural Language происходит поиск его синонимов.

Кнопка «Find synonyms» запускает определение языка, к которому принадлежит слово, и, если он найден, проверяет наличие или отсутствие слова в словаре языка. Затем извлекается коллекция похожих слов, отображаемая в виде списка.

Если синонимы не найдены или слово не распознано, выдается сообщение об ошибке.

Источник

Exit mobile version