Сгенерируйте зависящий от домена словарь чувства

В этом примере показано, как сгенерировать словарь для анализа мнений с помощью 10-K и 10-Q финансовых отчетов.

Анализ мнений позволяет вам автоматически обобщать чувство в данной части текста. Например, присвойте части текста "Эта компания, показывает устойчивый рост". и "Эта другая компания обвиняется во вводящих в заблуждение потребителях". с положительным и отрицательным чувством, соответственно. Кроме того, например, чтобы присвоить текст "Эта компания показывает чрезвычайно устойчивый рост". более сильный счет чувства, чем текст "Эта компания показывает устойчивый рост".

Алгоритмы анализа мнений, такие как VADER используют аннотируемые списки названных словарей чувства слов. Например, VADER использует словарь чувства со словами, аннотируемыми счетом чувства в пределах от-1 к 1, где баллы близко к 1 указывают на сильное положительное чувство, баллы близко к-1 указывают на сильное отрицательное чувство, и баллы близко к нулю указывают на нейтральное чувство.

Чтобы анализировать чувство текста с помощью алгоритма VADER, используйте vaderSentimentScores функция. Если словарь чувства используется vaderSentimentScores функция не удовлетворяет данным, которые вы анализируете, например, если у вас есть проблемно-ориентированный набор данных как медицинские или технические данные, затем можно сгенерировать собственный словарь чувства с помощью маленького набора слов seed.

В этом примере показано, как сгенерировать словарь чувства, учитывая набор слов seed с помощью основанного на графике подхода на основе [1]:

  • Обучите слово, встраивающее, это моделирует подобие между словами с помощью обучающих данных.

  • Создайте упрощенный график, представляющий встраивание узлами, соответствующими словам и ребрам, взвешенным подобием.

  • Чтобы определить слова с сильной полярностью, идентифицируйте слова, соединенные с несколькими словами seed через короткие но в большой степени взвешенные пути.

Загрузка данных

Загрузите 10-K и 10-Q данные о финансовых отчетах Комиссии по ценным бумагам и биржам (SEC) через Электронный Сбор данных, Анализ и Извлечение (EDGAR) API [2] с помощью financeReports функция помощника присоединяется к этому примеру как вспомогательный файл. Чтобы получить доступ к этому файлу, откройте этот пример как Live Script. financeReports функционируйте загружает 10-K и 10-Q отчеты в течение заданного года, четверти и максимальной длины знака.

Загрузите набор 20 000 отчетов от четвертого квартала 2 019. В зависимости от размеров отчетов это может занять время, чтобы запуститься.

year = 2019;
qtr = 4;
textData = financeReports(year,qtr,'MaxNumReports',20000);
Downloading 10-K and 10-Q reports...
Done.
Elapsed time is 1799.718710 seconds.

Задайте наборы положительных и отрицательных слов seed, чтобы использовать с этими данными. Слова seed должны появиться, по крайней мере, однажды в текстовых данных, в противном случае они проигнорированы.

seedsPositive = ["achieve" "advantage" "better" "creative" "efficiency" ...
    "efficiently" "enhance" "greater" "improved" "improving" ...
    "innovation" "innovations" "innovative" "opportunities" "profitable" ...
    "profitably" "strength" "strengthen" "strong" "success"]';

seedsNegative = ["adverse" "adversely" "against" "complaint" "concern" ...
    "damages" "default" "deficiencies" "disclosed" "failure" ...
    "fraud" "impairment" "litigation" "losses" "misleading" ...
    "omit" "restated" "restructuring" "termination" "weaknesses"]';

Подготовьте текстовые данные

Создайте имена функций preprocessText это готовит текстовые данные к анализу. preprocessText функция, перечисленная в конце примера, выполняет следующие шаги:

  • Сотрите любые URL.

  • Маркируйте текст.

  • Удалите лексемы, содержащие цифры.

  • Преобразуйте текст в нижний регистр.

  • Удалите любые слова с двумя или меньшим количеством символов.

  • Удалите любые слова остановки.

Предварительно обработайте текст с помощью preprocessText функция. В зависимости от размера текстовых данных это может занять время, чтобы запуститься.

documents = preprocessText(textData);

Визуализируйте предварительно обработанные текстовые данные, одним словом, облако.

figure
wordcloud(documents);

Обучите Word Embedding

Вложения Word сопоставляют слова в словаре к числовым векторам. Эти вложения могут получить семантические детали слов так, чтобы подобные слова имели подобные векторы.

Обучите слово, встраивающее, это моделирует подобие между словами с помощью обучающих данных. Задайте окно контекста размера 25 и отбросьте слова, которые появляются меньше чем 20 раз. В зависимости от размера текстовых данных это может занять время, чтобы запуститься.

emb = trainWordEmbedding(documents,'Window',25,'MinCount',20);
Training: 100% Loss: 1.44806  Remaining time: 0 hours 0 minutes.

Создайте Word Graph

Создайте упрощенный график, представляющий встраивание узлами, соответствующими словам и ребрам, взвешенным подобием.

Создайте взвешенный график с узлами, соответствующими словам в словаре, ребра, обозначающие, являются ли слова в окружении 7 друг из друга и весах, соответствующих расстоянию косинуса между соответствующими векторами слова во встраивании.

Для каждого слова в словаре найдите самые близкие 7 слов и их расстояния косинуса.

numNeighbors = 7;
vocabulary = emb.Vocabulary;
wordVectors = word2vec(emb,vocabulary);

[nearestWords,dist] = vec2word(emb,wordVectors,numNeighbors);

Чтобы создать график, используйте graph функционируйте и задайте попарные входные и выходные узлы и задайте их вес ребра.

Задайте входные и выходные узлы.

sourceNodes = repelem(vocabulary,numNeighbors);
targetNodes = reshape(nearestWords,1,[]);

Вычислите вес ребра.

edgeWeights = reshape(dist,1,[]);

Создайте график, соединяющий каждое слово с его соседями с весом ребра, соответствующим баллам подобия.

wordGraph = graph(sourceNodes,targetNodes,edgeWeights,vocabulary);

Удалите повторные ребра с помощью simplify функция.

wordGraph = simplify(wordGraph);

Визуализируйте раздел графика слова, соединенного со словом "потери".

word = "losses";
idx = findnode(wordGraph,word);
nbrs = neighbors(wordGraph,idx);
wordSubgraph = subgraph(wordGraph,[idx; nbrs]);
figure
plot(wordSubgraph)
title("Words connected to """ + word + """")

Сгенерируйте баллы чувства

Чтобы определить слова с сильной полярностью, идентифицируйте слова, соединенные с несколькими словами seed через короткие но в большой степени взвешенные пути.

Инициализируйте массив баллов чувства, соответствующих каждому слову в словаре.

sentimentScores = zeros([1 numel(vocabulary)]);

Итеративно пересеките график и обновите баллы чувства.

Пересеките график на различных глубинах. Для каждой глубины вычислите положительную и отрицательную полярность слов при помощи положительных и отрицательных seed, чтобы распространить чувство к остальной части графика.

Для каждой глубины:

  • Вычислите положительные и отрицательные баллы полярности.

  • Объясните различие в общей массе положительного и отрицательного потока в графике.

  • Для каждого слова узла нормируйте различие его двух баллов.

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

Задайте максимальную длину пути 4.

maxPathLength = 4;

Итеративно пересеките график и вычислите сумму баллов чувства.

for depth = 1:maxPathLength
    
    % Calculate polarity scores.
    polarityPositive = polarityScores(seedsPositive,vocabulary,wordGraph,depth);
    polarityNegative = polarityScores(seedsNegative,vocabulary,wordGraph,depth);
    
    % Account for difference in overall mass of positive and negative flow
    % in the graph.
    b = sum(polarityPositive) / sum(polarityNegative);
        
    % Calculate new sentiment scores.
    sentimentScoresNew = polarityPositive - b * polarityNegative;
    sentimentScoresNew = normalize(sentimentScoresNew,'range',[-1,1]);
    
    % Add scores to sum.
    sentimentScores = sentimentScores + sentimentScoresNew;
end

Нормируйте баллы чувства на количество итераций.

sentimentScores = sentimentScores / maxPathLength;

Составьте таблицу, содержащую словарь и соответствующие баллы чувства.

tbl = table;
tbl.Token = vocabulary';
tbl.SentimentScore = sentimentScores';

Чтобы удалить лексемы с нейтральным чувством от словаря, удалите лексемы со счетом чувства, которые имеют абсолютное значение меньше, чем порог 0,1.

thr = 0.1;
idx = abs(tbl.SentimentScore) < thr;
tbl(idx,:) = [];

Сортировка строк таблицы убывающим чувством выигрывает и просматривает первые несколько строк.

tbl = sortrows(tbl,'SentimentScore','descend');
head(tbl)
ans=8×2 table
         Token         SentimentScore
    _______________    ______________

    "opportunities"       0.95633    
    "innovative"          0.89635    
    "success"             0.84362    
    "focused"             0.83768    
    "strong"              0.81042    
    "capabilities"        0.79174    
    "innovation"          0.77698    
    "improved"            0.77176    

Можно использовать эту таблицу в качестве пользовательского словаря чувства для vaderSentimentScores функция.

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

figure
subplot(1,2,1);
idx = tbl.SentimentScore > 0;
tblPositive = tbl(idx,:);
wordcloud(tblPositive,'Token','SentimentScore')
title('Positive Words')

subplot(1,2,2);
idx = tbl.SentimentScore < 0;
tblNegative = tbl(idx,:);
tblNegative.SentimentScore = abs(tblNegative.SentimentScore);
wordcloud(tblNegative,'Token','SentimentScore')
title('Negative Words')

Экспортируйте таблицу в файл CSV.

filename = "financeSentimentLexicon.csv";
writetable(tbl,filename)

Анализируйте чувство в тексте

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

Создайте массив строк, содержащий текстовые данные, и предварительно обработайте их с помощью preprocessText функция.

textDataNew = [
    "This innovative company is continually showing strong growth."
    "This other company is accused of misleading consumers."];
documentsNew = preprocessText(textDataNew);

Оцените чувство с помощью vaderSentimentScores функция. Задайте словарь чувства, созданный в этом примере с помощью 'SentimentLexicon' опция.

compoundScores = vaderSentimentScores(documentsNew,'SentimentLexicon',tbl)
compoundScores = 2×1

    0.4360
   -0.1112

Положительные и отрицательные баллы указывают на положительное и отрицательное чувство, соответственно. Величина значения соответствует силе чувства.

Вспомогательные Функции

Текст, предварительно обрабатывающий функцию

preprocessText функция выполняет следующие шаги:

  • Сотрите любые URL.

  • Маркируйте текст.

  • Удалите лексемы, содержащие цифры.

  • Преобразуйте текст в нижний регистр.

  • Удалите любые слова с двумя или меньшим количеством символов.

  • Удалите любые слова остановки.

function documents = preprocessText(textData)

% Erase URLS.
textData = eraseURLs(textData);

% Tokenize.
documents = tokenizedDocument(textData);

% Remove tokens containing digits.
pat = textBoundary + wildcardPattern + digitsPattern + wildcardPattern + textBoundary;
documents = replace(documents,pat,"");

% Convert to lowercase.
documents = lower(documents);

% Remove short words.
documents = removeShortWords(documents,2);

% Remove stop words.
documents = removeStopWords(documents);

end

Функция баллов полярности

polarityScores функция возвращает вектор из баллов полярности, учитывая набор слов seed, словаря, графика и заданной глубины. Функция вычисляет сумму по максимальному взвешенному пути от каждого слова seed до каждого узла в словаре. Высокий счет полярности указывает на фразы, соединенные с несколькими словами seed и через короткие и через строго взвешенные пути.

Функция выполняет следующие шаги:

  • Инициализируйте множество seed с единицами и в противном случае нулями.

  • Цикл по seed. Для каждого seed итеративно пересеките график на различных уровнях глубины. Для первой итерации, набор пространство поиска к ближайшим соседям seed.

  • Для каждого уровня глубины, цикла по узлам в пространстве поиска и идентифицируют его соседей в графике.

  • Цикл по его соседям и обновлению соответствующие баллы. Обновленный счет является максимальным значением текущего счета к seed и соседу, и счета к seed и поисковому узлу, взвешенному соответствующим ребром графика.

  • В конце поиска уровня глубины добавьте соседей пространства поиска. Это увеличивает глубину поиска следующей итерации.

Выходная полярность является суммой баллов, соединенных с входными seed.

function polarity = polarityScores(seeds,vocabulary,wordGraph,depth)

% Remove seeds missing from vocabulary.
idx = ~ismember(seeds,vocabulary);
seeds(idx) = [];

% Initialize scores.
vocabularySize = numel(vocabulary);
scores = zeros(vocabularySize);
idx = ismember(vocabulary,seeds);
scores(idx,idx) = eye(numel(seeds));

% Loop over seeds.
for i = 1:numel(seeds)
    
    % Initialize search space.
    seed = seeds(i);
    idxSeed = vocabulary == seed;
    searchSpace = find(idxSeed);
    
    % Search at different depths.
    for d = 1:depth
    
        % Loop over nodes in search space.
        numNodes = numel(searchSpace);
        
        for k = 1:numNodes
            
            idxNew = searchSpace(k);
            
            % Find neighbors and weights.
            nbrs = neighbors(wordGraph,idxNew);
            idxWeights = findedge(wordGraph,idxNew,nbrs);
            weights = wordGraph.Edges.Weight(idxWeights);
            
            % Loop over neighbors.
            for j = 1:numel(nbrs)
                
                % Calculate scores.
                score = scores(idxSeed,nbrs(j));
                scoreNew = scores(idxSeed,idxNew);
                
                % Update score.
                scores(idxSeed,nbrs(j)) = max(score,scoreNew*weights(j));
            end
            
            % Appended nodes to search space for next depth iteration.
            searchSpace = [searchSpace nbrs'];
        end
    end
end

% Find seeds in vocabulary.
[~,idx] = ismember(seeds,vocabulary);

% Sum scores connected to seeds.
polarity = sum(scores(idx,:));

end

Библиография

  1. Великович, Lenid. "Жизнеспособность выведенных из сети Словарей Полярности". В Продолжениях Ежегодной конференции североамериканской Главы Ассоциации для Компьютерной лингвистики, 2010, стр 777-785. 2010.

  2. Доступ Данные EDGAR. https://www.sec.gov/os/accessing-edgar-data