Advertisement
Guest User

Untitled

a guest
Oct 16th, 2019
194
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 31.49 KB | None | 0 0
  1.  public class NewFieldTaggingManager
  2.     {
  3.         /// <summary>
  4.         /// Назнвание игнорируемой технической сущности.
  5.         /// </summary>
  6.         public const string Other = "<Other>";
  7.  
  8.         /// <summary>
  9.         /// Порог для отбора достоверных значений.
  10.         /// </summary>
  11.         public const double threshold = 0.6;
  12.  
  13.         /// <summary>
  14.         /// результат сравнения двух фарз.
  15.         /// </summary>
  16.         private enum CompareStatus
  17.         {
  18.             None,
  19.             Better,
  20.             Worse,
  21.             Questionable
  22.         }
  23.  
  24.         /// <summary>
  25.         /// Представляет диапазон индексов.
  26.         /// </summary>
  27.         private class IndexRange
  28.         {
  29.             public int Start { get; }
  30.             public int Length { get; set; }
  31.  
  32.             public IndexRange(int start, int length)
  33.             {
  34.                 Start = start;
  35.                 Length = length;
  36.             }
  37.             public override string ToString() => $"{Start};{Length}";
  38.         }
  39.  
  40.         /// <summary>
  41.         /// Содержит данные о фразе.
  42.         /// </summary>
  43.         private class WordsPhrase
  44.         {
  45.             /// <summary>
  46.             /// Родительская фраза в процессе разбора.
  47.             /// </summary>
  48.             public WordsPhrase Parent { get; set; }
  49.  
  50.             /// <summary>
  51.             /// Текст фарзы.
  52.             /// </summary>
  53.             public string Phrase { get; }
  54.  
  55.             /// <summary>
  56.             /// Вес (уверенность)
  57.             /// </summary>
  58.             public double Weight { get; }
  59.  
  60.             /// <summary>
  61.             /// Метка (имя сущности).
  62.             /// </summary>
  63.             public string Label { get; }
  64.  
  65.             /// <summary>
  66.             /// Токены которые принадлежат к фразе.
  67.             /// </summary>
  68.             public List<Token> Tokens { get; }
  69.  
  70.             /// <summary>
  71.             /// Топовые сущности.
  72.             /// </summary>
  73.             public List<Tuple<string, double>> TopLabels { get; }
  74.  
  75.             public CompareStatus Status { get; set; }
  76.  
  77.             /// <summary>
  78.             /// Поколение разметки на котором сгенерирована фраза.
  79.             /// </summary>
  80.             public int Generation { get; set; }
  81.  
  82.             public WordsPhrase(string phrase, string label, double weight, List<Token> tokens, List<Tuple<string, double>> topLabels)
  83.             {
  84.                 Phrase = phrase;
  85.                 Label = label;
  86.                 Weight = weight;
  87.                 Tokens = tokens;
  88.                 TopLabels = topLabels;
  89.             }
  90.  
  91.             public WordsPhrase Clone()
  92.             {
  93.                 return (WordsPhrase)MemberwiseClone();
  94.             }
  95.  
  96.             public override string ToString() => $"{Phrase} => {Label} ({Weight:N3})";
  97.         }
  98.  
  99.         /// <summary>
  100.         /// Выполняет определение принадлежности токенов к определенной сущности.
  101.         /// </summary>
  102.         /// <param name="tokens"></param>
  103.         public void Parse(List<Token> tokens, ClassifierEngineData engineData)
  104.         {
  105.             var groups = TagTokenHelper.GetMaxGroups(tokens,
  106.                 t => t.TokenType == TokenType.Undefined,
  107.                 t => t.TokenType == TokenType.Ignore
  108.             );
  109.             foreach (var group in groups)
  110.             {
  111.                 // фразы в скобочках обрабатываем отдельно
  112.                 if (group.Count > 1 && IsGroupInParentheses(group, tokens))
  113.                     AnalizeAndTagSentenceInParentheses(group, tokens, engineData);
  114.                 else
  115.                     AnalizeAndTagSentence(group, tokens, engineData);
  116.             }
  117.         }
  118.  
  119.         /// <summary>
  120.         /// Проверяет находится ли группа в скобках.
  121.         /// </summary>
  122.         /// <param name="group"></param>
  123.         /// <param name="tokens"></param>
  124.         /// <returns></returns>
  125.         private bool IsGroupInParentheses(List<Token> group, List<Token> tokens)
  126.         {
  127.             // находим левый токен и пропускаем игнорируемые
  128.             int firstGroupTokenIdx = tokens.IndexOf(group[0]);
  129.             while (firstGroupTokenIdx - 1 >= 0 && tokens[firstGroupTokenIdx - 1].TokenType == TokenType.Ignore)
  130.                 firstGroupTokenIdx--;
  131.  
  132.             if (firstGroupTokenIdx == 0)
  133.                 return false;
  134.  
  135.             // находим правый токен и пропускаем игнорируемые
  136.             int lastGroupTokenIdx = tokens.IndexOf(group[group.Count - 1]);
  137.             while (lastGroupTokenIdx + 1 < tokens.Count && tokens[lastGroupTokenIdx + 1].TokenType == TokenType.Ignore)
  138.                 lastGroupTokenIdx++;
  139.  
  140.             if (lastGroupTokenIdx == tokens.Count - 1)
  141.                 return false;
  142.  
  143.             // проверяем что дальше стоят скобочки
  144.             if (tokens[firstGroupTokenIdx - 1].Word == "(" && tokens[lastGroupTokenIdx + 1].Word == ")")
  145.                 return true;
  146.  
  147.             return false;
  148.         }
  149.  
  150.         /// <summary>
  151.         /// Выполняет анализ списка токенов, которые расположены в скобках и являются (скорей всего) одной сущностью.
  152.         /// </summary>
  153.         /// <param name="group"></param>
  154.         /// <param name="tokens"></param>
  155.         private void AnalizeAndTagSentenceInParentheses(List<Token> group, List<Token> tokens, ClassifierEngineData engineData)
  156.         {
  157.             WordsPhrase phrase = TokensToPhrase(group, tokens, engineData);
  158.  
  159.             group.ForEach(t => { t.MatchWord = phrase.TopLabels[0].Item1; t.TokenType = TokenType.Param; });
  160.         }
  161.  
  162.         /// <summary>
  163.         /// Выполняет анализ списка токенов для определения принадлежности к сущности.
  164.         /// </summary>
  165.         /// <param name="tokens"></param>
  166.         private void AnalizeAndTagSentence(List<Token> tokens, List<Token> sourceTokens, ClassifierEngineData engineData)
  167.         {
  168.             // генерируем стартовый набор фраз
  169.             List<WordsPhrase> phrases = GenerateInitialPhrases(tokens, sourceTokens, engineData);
  170.  
  171.             // циклически повторяем процесс присоединения соседних токенов к фразам
  172.             int gen = 1;                // поколение генерации+обработки новых фраз
  173.             const int MaxGenCount = 5;  // максимальное кол-во повторений для избегания очень долгих циклов на говно-текстах.
  174.             bool done = false;          // флаг остановки процесса
  175.             while (gen <= MaxGenCount && !done)
  176.             {
  177.                 phrases = ProcessExpandPhrases(phrases, sourceTokens, engineData, gen, out done);
  178.                 gen++;
  179.             }
  180.  
  181.             // помечаем токены для найденых фраз
  182.             foreach (var phrase in phrases)
  183.             {
  184.                 foreach (var token in phrase.Tokens)
  185.                 {
  186.                     if (phrase.Label != Other && phrase.Weight > threshold)
  187.                     {
  188.                         token.TokenType = TokenType.Param;
  189.                         token.MatchWord = phrase.Label;
  190.                     }
  191.                     else
  192.                     {
  193.                         //token.MatchWord = "Other";
  194.                     }
  195.                 }
  196.             }
  197.         }
  198.  
  199.         /// <summary>
  200.         /// Генерирует фразу со всеми характеристиками из списка токенов.
  201.         /// </summary>
  202.         /// <param name="phraseTokens"></param>
  203.         /// <returns></returns>
  204.         private static WordsPhrase TokensToPhrase(List<Token> phraseTokens, List<Token> sourceTokens, ClassifierEngineData engineData)
  205.         {
  206.             int takeCount = phraseTokens.Count;
  207.             List<Token> allPhraseTokens = GetAllTokensWithSeparators(phraseTokens, sourceTokens);
  208.             string sumWord = TagTokenHelper.RecollectTagsWord(allPhraseTokens);
  209.             var res = engineData.Predict(sumWord);
  210.  
  211.             double degreOfConfidence = res.Weight;
  212.             List<Tuple<string, double>> topCfSum = GetTopFields(engineData.AllLabels, res.Score, 3);
  213.  
  214.             WordsPhrase phrase = new WordsPhrase(sumWord, res.Result, res.Weight, phraseTokens, topCfSum);
  215.             return phrase;
  216.         }
  217.  
  218.         /// <summary>
  219.         /// Генерирует стартовый набор фраз.
  220.         /// </summary>
  221.         /// <param name="tokens"></param>
  222.         /// <param name="sourceTokens"></param>
  223.         /// <param name="engineData"></param>
  224.         /// <returns></returns>
  225.         private List<WordsPhrase> GenerateInitialPhrases(List<Token> tokens, List<Token> sourceTokens, ClassifierEngineData engineData)
  226.         {
  227.             List<WordsPhrase> wordsPhrases = new List<WordsPhrase>();
  228.  
  229.             // определяем и учитываем токены написанные слитно друг с другом.
  230.             List<int> continiousTokensIndexes = DetectContiniousTokens(tokens, sourceTokens);
  231.             var continiousTokensRanges = CollectContiniousRanges(continiousTokensIndexes).ToDictionary(r => r.Start);
  232.  
  233.             for (int i = 0; i < tokens.Count;)
  234.             {
  235.                 int start = i;
  236.                 int len = 1;
  237.                 if (continiousTokensRanges.TryGetValue(start, out var range))
  238.                 {
  239.                     len = range.Length;
  240.                 }
  241.  
  242.                 List<Token> phraseTokens = tokens.Skip(start).Take(len).ToList();
  243.                 WordsPhrase phrase = TokensToPhrase(phraseTokens, sourceTokens, engineData);
  244.                 //phrase.Status = CompareStatus.Better;
  245.                 wordsPhrases.Add(phrase);
  246.                 i += len;
  247.             }
  248.             return wordsPhrases;
  249.         }
  250.  
  251.         /// <summary>
  252.         /// Выполняет расширение фраз и их анализ.
  253.         /// </summary>
  254.         /// <param name="phrases"></param>
  255.         /// <param name="sourceTokens"></param>
  256.         /// <param name="engineData"></param>
  257.         /// <param name="generation"></param>
  258.         /// <param name="done"></param>
  259.         /// <returns></returns>
  260.         private List<WordsPhrase> ProcessExpandPhrases(List<WordsPhrase> phrases, List<Token> sourceTokens, ClassifierEngineData engineData, int generation, out bool done)
  261.         {
  262.             List<WordsPhrase> childPhrases = new List<WordsPhrase>();
  263.             bool newPhrasesGenerated = false;
  264.             for (int i = 0; i < phrases.Count; i++)
  265.             {
  266.                 var phrase = phrases[i];
  267.  
  268.                 // если фраза была сгенерирована ранее чем на прошлом поколении, то нет смысла обрабатывать, т.к. она точно сгенерирует изменений.
  269.                 if (phrase.Generation < generation - 1)
  270.                 {
  271.                     childPhrases.Add(phrase);
  272.                     continue;
  273.                 }
  274.  
  275.                 bool anyChildrenAdded = false;
  276.                 // после 1го поколения расширяемся только на Other
  277.                 // расширяем фразу влево
  278.                 if (i > 0 && (generation == 1 || phrases[i - 1].Label == Other))
  279.                 {
  280.                     WordsPhrase combinedToLeft = ConcatenatePhrases(phrases[i - 1], phrase, sourceTokens, engineData);
  281.                     var compareStatus = ComparePhrases(combinedToLeft, phrase);
  282.                     combinedToLeft.Parent = phrase;
  283.                     combinedToLeft.Status = compareStatus;
  284.                     combinedToLeft.Generation = generation;
  285.                     childPhrases.Add(combinedToLeft);
  286.                     anyChildrenAdded = true;
  287.                 }
  288.  
  289.                 // расширяем фразу вправо
  290.                 if (i < phrases.Count - 1 && (generation == 1 || phrases[i + 1].Label == Other))
  291.                 {
  292.                     WordsPhrase combinedToRight = ConcatenatePhrases(phrase, phrases[i + 1], sourceTokens, engineData);
  293.                     var compareStatus = ComparePhrases(combinedToRight, phrase);
  294.                     combinedToRight.Parent = phrase;
  295.                     combinedToRight.Status = compareStatus;
  296.                     combinedToRight.Generation = generation;
  297.                     childPhrases.Add(combinedToRight);
  298.                     anyChildrenAdded = true;
  299.                 }
  300.  
  301.                 // если расширение не произошло, то просто добавляем фразу в результат
  302.                 if (!anyChildrenAdded)
  303.                     childPhrases.Add(phrase);
  304.                 else
  305.                     newPhrasesGenerated = true;
  306.             }
  307.  
  308.             // если нового ничего не сгенерировано, то сигнализируем об этом
  309.             if (!newPhrasesGenerated)
  310.             {
  311.                 done = true;
  312.                 return childPhrases;
  313.             }
  314.  
  315.             // выбираем лучшие фразы из одинаковых или родственных вариантов
  316.             var res1 = ResolveSamePhrasePairs(childPhrases);
  317.             var res2 = ResolveSiblings(res1);
  318.  
  319.             // циклически выбираем лучшие фразы из пересекающихся вариантов
  320.             var res = res2;
  321.             bool hasChanges;
  322.             do
  323.             {
  324.                 res = ResolveOverlaid(res, out hasChanges);
  325.             } while (hasChanges);
  326.  
  327.             done = false;
  328.             return res;
  329.         }
  330.  
  331.         /// <summary>
  332.         /// Анализирует соседние фразы совпадающие по тексту.
  333.         /// </summary>
  334.         /// <param name="phrases"></param>
  335.         /// <returns></returns>
  336.         private List<WordsPhrase> ResolveSamePhrasePairs(List<WordsPhrase> phrases)
  337.         {
  338.             List<WordsPhrase> result = new List<WordsPhrase>();
  339.             for (int i = 0; i < phrases.Count;)
  340.             {
  341.                 if (i == phrases.Count - 1)
  342.                 {
  343.                     result.Add(phrases[i]);
  344.                     i += 1;
  345.                     continue;
  346.                 }
  347.  
  348.                 var phrase1 = phrases[i];
  349.                 var phrase2 = phrases[i + 1];
  350.  
  351.                 if (phrase1.Phrase != phrase2.Phrase)
  352.                 {
  353.                     result.Add(phrases[i]);
  354.                     i += 1;
  355.                     continue;
  356.                 }
  357.  
  358.                 ResolvePair(result, phrase1, phrase2);
  359.  
  360.                 i += 2;
  361.             }
  362.             RemoveFullDuplicates(result);
  363.             return result;
  364.         }
  365.  
  366.         /// <summary>
  367.         /// Анализирует соседние фразы с общим родителем.
  368.         /// </summary>
  369.         /// <param name="phrases"></param>
  370.         /// <returns></returns>
  371.         private List<WordsPhrase> ResolveSiblings(List<WordsPhrase> phrases)
  372.         {
  373.             List<WordsPhrase> result = new List<WordsPhrase>();
  374.             for (int i = 0; i < phrases.Count;)
  375.             {
  376.                 if (i == phrases.Count - 1)
  377.                 {
  378.                     result.Add(phrases[i]);
  379.                     i += 1;
  380.                     continue;
  381.                 }
  382.  
  383.                 var phrase1 = phrases[i];
  384.                 var phrase2 = phrases[i + 1];
  385.  
  386.                 if (phrase1.Parent == null || phrase1.Parent != phrase2.Parent)
  387.                 {
  388.                     result.Add(phrases[i]);
  389.                     i += 1;
  390.                     continue;
  391.                 }
  392.  
  393.                 ResolvePair(result, phrase1, phrase2);
  394.  
  395.                 i += 2;
  396.             }
  397.             RemoveFullDuplicates(result);
  398.             return result;
  399.         }
  400.  
  401.         /// <summary>
  402.         /// Анализирует соседние фразы учитывая наложение токенов.
  403.         /// </summary>
  404.         /// <param name="phrases"></param>
  405.         /// <param name="hasChanges"></param>
  406.         /// <returns></returns>
  407.         private List<WordsPhrase> ResolveOverlaid(List<WordsPhrase> phrases, out bool hasChanges)
  408.         {
  409.             hasChanges = false;
  410.             List<WordsPhrase> result = new List<WordsPhrase>();
  411.             for (int i = 0; i < phrases.Count;)
  412.             {
  413.                 if (i == phrases.Count - 1)
  414.                 {
  415.                     result.Add(phrases[i]);
  416.                     i += 1;
  417.                     continue;
  418.                 }
  419.  
  420.                 var phrase1 = phrases[i];
  421.                 var phrase2 = phrases[i + 1];
  422.  
  423.                 if (!IsTokensOverlaids(phrase1.Tokens, phrase2.Tokens))
  424.                 {
  425.                     result.Add(phrase1);
  426.                     i += 1;
  427.                     continue;
  428.                 }
  429.  
  430.                 hasChanges = true;
  431.                 ResolvePair(result, phrase1, phrase2);
  432.  
  433.                 i += 2;
  434.             }
  435.             RemoveFullDuplicates(result);
  436.             return result;
  437.         }
  438.  
  439.         /// <summary>
  440.         /// Анализирует 2 фразы и результат их сравнения кладет в <paramref name="result"/>.
  441.         /// </summary>
  442.         /// <param name="result"></param>
  443.         /// <param name="phrase1"></param>
  444.         /// <param name="phrase2"></param>
  445.         private static void ResolvePair(List<WordsPhrase> result, WordsPhrase phrase1, WordsPhrase phrase2)
  446.         {
  447.             var status1 = phrase1.Status;
  448.             var status2 = phrase2.Status;
  449.  
  450.             if (phrase1.Status == phrase2.Status)
  451.             {
  452.                 if (status1 == CompareStatus.Worse)
  453.                 {
  454.                     // откатываем
  455.                     result.Add(phrase1.Parent);
  456.                     result.Add(phrase2.Parent);
  457.                 }
  458.                 else if (status1 == CompareStatus.Questionable)
  459.                 {
  460.                     // берем с лучшим весом, если превышает порог или откатываем
  461.                     double maxWeight = Math.Max(phrase1.Weight, phrase2.Weight);
  462.  
  463.                     if (maxWeight > threshold)
  464.                     {
  465.                         var tmpPhrase = GetWhere(phrase1, phrase2, p => p.Weight == maxWeight);
  466.                         result.Add(tmpPhrase);
  467.                     }
  468.                     else
  469.                     {
  470.                         result.Add(phrase1.Parent);
  471.                         result.Add(phrase2.Parent);
  472.                     }
  473.                 }
  474.                 else if (status1 == CompareStatus.Better)
  475.                 {
  476.                     // кладем с большим весом
  477.                     var tmpPhrase = phrase1.Weight >= phrase2.Weight ? phrase1 : phrase2;
  478.                     result.Add(tmpPhrase);
  479.                 }
  480.                 else // None
  481.                 {
  482.                     // ничего не меняем
  483.                     result.Add(phrase1);
  484.                     result.Add(phrase2);
  485.                 }
  486.             }
  487.             else // status1 != status2
  488.             {
  489.                 if (IsOneEquals(status1, status2, CompareStatus.Questionable))
  490.                 {
  491.                     if (IsOneEquals(status1, status2, CompareStatus.Better))
  492.                     {
  493.                         // просто берем лучше
  494.                         var betterPhrase = GetWhere(phrase1, phrase2, s => s.Status == CompareStatus.Better);
  495.                         result.Add(betterPhrase);
  496.                     }
  497.                     else if (IsOneEquals(status1, status2, CompareStatus.Worse))
  498.                     {
  499.                         // сразу откат
  500.                         result.Add(phrase1.Parent ?? phrase1);
  501.                         result.Add(phrase2.Parent ?? phrase2);
  502.                     }
  503.                     else
  504.                     {
  505.                         // если Questionable переваливает за порог, то берем
  506.                         var questionablePhrase = GetWhere(phrase1, phrase2, s => s.Status == CompareStatus.Questionable);
  507.                         if (questionablePhrase.Weight > threshold)
  508.                         {
  509.                             result.Add(questionablePhrase);
  510.                         }
  511.                         else
  512.                         {
  513.                             result.Add(phrase1.Parent ?? phrase1);
  514.                             result.Add(phrase2.Parent ?? phrase2);
  515.                         }
  516.                     }
  517.                 }
  518.                 else if (IsOneEquals(status1, status2, CompareStatus.Better))
  519.                 {
  520.                     // просто берем учший
  521.                     var betterPhrase = GetWhere(phrase1, phrase2, s => s.Status == CompareStatus.Better);
  522.                     result.Add(betterPhrase);
  523.                 }
  524.                 else // worse или none - откат
  525.                 {
  526.                     result.Add(phrase1.Parent ?? phrase1);
  527.                     result.Add(phrase2.Parent ?? phrase2);
  528.                 }
  529.             }
  530.         }
  531.  
  532.         /// <summary>
  533.         /// Удаляет полные дубликаты соседних токенов.
  534.         /// </summary>
  535.         /// <param name="phrases"></param>
  536.         private void RemoveFullDuplicates(List<WordsPhrase> phrases)
  537.         {
  538.             for (int i = 0; i < phrases.Count - 1; i++)
  539.             {
  540.                 var phrase1 = phrases[i];
  541.                 var phrase2 = phrases[i + 1];
  542.                 if (phrase1 == phrase2)
  543.                 {
  544.                     phrases.RemoveAt(i + 1);
  545.                 }
  546.             }
  547.         }
  548.  
  549.         /// <summary>
  550.         /// Возвращает содержатся ли пересекающиеся токены.
  551.         /// </summary>
  552.         /// <param name="tokens1"></param>
  553.         /// <param name="tokens2"></param>
  554.         /// <returns></returns>
  555.         private static bool IsTokensOverlaids(List<Token> tokens1, List<Token> tokens2)
  556.         {
  557.             for (int i = 0; i < tokens1.Count; i++)
  558.             {
  559.                 for (int j = 0; j < tokens2.Count; j++)
  560.                 {
  561.                     if (tokens1[i] == tokens2[j])
  562.                         return true;
  563.                 }
  564.             }
  565.             return false;
  566.         }
  567.  
  568.         /// <summary>
  569.         /// Из 2х объектов выбирает удовлетворяющий <paramref name="predicate"/>.
  570.         /// </summary>
  571.         /// <typeparam name="T"></typeparam>
  572.         /// <param name="obj1"></param>
  573.         /// <param name="obj2"></param>
  574.         /// <param name="predicate"></param>
  575.         /// <returns></returns>
  576.         private static T GetWhere<T>(T obj1, T obj2, Predicate<T> predicate)
  577.             where T : class
  578.         {
  579.             if (predicate(obj1))
  580.                 return obj1;
  581.             if (predicate(obj2))
  582.                 return obj2;
  583.             return null;
  584.         }
  585.  
  586.         /// <summary>
  587.         /// Совпадает ли что либо с <paramref name="toCompare"/>.
  588.         /// </summary>
  589.         /// <param name="status1"></param>
  590.         /// <param name="status2"></param>
  591.         /// <param name="toCompare"></param>
  592.         /// <returns></returns>
  593.         private static bool IsOneEquals(CompareStatus status1, CompareStatus status2, CompareStatus toCompare)
  594.         {
  595.             return status1 == toCompare || status2 == toCompare;
  596.         }
  597.  
  598.         const double epsilon = 0;//-0.002;
  599.  
  600.         /// <summary>
  601.         /// Сравнивает новую и старую фарзы
  602.         /// </summary>
  603.         /// <param name="newPhrase"></param>
  604.         /// <param name="oldPhrase"></param>
  605.         /// <returns></returns>
  606.         private CompareStatus ComparePhrases(WordsPhrase newPhrase, WordsPhrase oldPhrase)
  607.         {
  608.             string newName = newPhrase.Label;
  609.             string oldName = oldPhrase.Label;
  610.             if (newName != Other && oldName != Other)
  611.             {
  612.                 if (newName == oldName)
  613.                 {
  614.                     double newConf = newPhrase.Weight;
  615.                     double oldConf = oldPhrase.Weight;
  616.  
  617.                     double delta = newConf - oldConf;
  618.  
  619.                     return delta > epsilon ? CompareStatus.Better : CompareStatus.Worse; // однозначно лучше, если уверенность больше
  620.                 }
  621.                 else
  622.                     return CompareStatus.Questionable;    // questionable
  623.             }
  624.  
  625.             if (newName == Other && oldName == Other)
  626.             {
  627.                 return CompareStatus.Worse;   // оба Other - нет смысла обрабатывать
  628.             }
  629.  
  630.             return CompareStatus.Questionable;    // если новый не Other то под вопросом
  631.         }
  632.  
  633.         /// <summary>
  634.         /// Склеивает две фразы в одну.
  635.         /// </summary>
  636.         /// <param name="phrase1"></param>
  637.         /// <param name="phrase2"></param>
  638.         /// <param name="sourceTokens"></param>
  639.         /// <param name="engineData"></param>
  640.         /// <returns></returns>
  641.         private static WordsPhrase ConcatenatePhrases(WordsPhrase phrase1, WordsPhrase phrase2, List<Token> sourceTokens, ClassifierEngineData engineData)
  642.         {
  643.             List<Token> sumTokens = phrase1.Tokens.Concat(phrase2.Tokens).ToList();
  644.             return TokensToPhrase(sumTokens, sourceTokens, engineData);
  645.         }
  646.  
  647.         /// <summary>
  648.         /// Собирает все токены из <paramref name="phraseTokens"/> вместе с пропущенными (игнорируемыми) используя все токены предложения <paramref name="sourceTokens"/>.
  649.         /// </summary>
  650.         /// <param name="phraseTokens"></param>
  651.         /// <param name="sourceTokens"></param>
  652.         /// <returns></returns>
  653.         private static List<Token> GetAllTokensWithSeparators(List<Token> phraseTokens, List<Token> sourceTokens)
  654.         {
  655.             if (phraseTokens.Count == 1)
  656.                 return phraseTokens;
  657.  
  658.             bool takeIt = false;
  659.             List<Token> result = new List<Token>();
  660.             for (int i = 0; i < sourceTokens.Count; i++)
  661.             {
  662.                 if (!takeIt)
  663.                 {
  664.                     if (sourceTokens[i] == phraseTokens[0])
  665.                     {
  666.                         takeIt = true;
  667.                         result.Add(sourceTokens[i]);
  668.                     }
  669.                 }
  670.                 else
  671.                 {
  672.                     result.Add(sourceTokens[i]);
  673.                     if (sourceTokens[i] == phraseTokens[phraseTokens.Count - 1])
  674.                         break;
  675.                 }
  676.             }
  677.  
  678.             return result;
  679.         }
  680.  
  681.         /// <summary>
  682.         /// Возвращает список из наиболее вероятных полей для данного слова.
  683.         /// </summary>
  684.         /// <param name="wordVec"></param>
  685.         /// <param name="topN"></param>
  686.         /// <returns></returns>
  687.         private static List<Tuple<string, double>> GetTopFields(string[] allLabels, float[] wordVec, int topN)
  688.         {
  689.             List<Tuple<string, double>> result = new List<Tuple<string, double>>();
  690.             for (int i = 0; i < wordVec.Length; i++)
  691.             {
  692.                 if (wordVec[i] > 0.0)
  693.                     result.Add(new Tuple<string, double>(allLabels[i], wordVec[i]));
  694.             }
  695.             return result.OrderByDescending(w => w.Item2).Take(topN).ToList();
  696.         }
  697.  
  698.         // предлоги которые "приклеиваем" к следующим словам
  699.         private static HashSet<string> prepositions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "with", "without", "w/", "w/o" };
  700.  
  701.         /// <summary>
  702.         /// Определяет токены написанные слитно друг с другом.
  703.         /// </summary>
  704.         /// <param name="tokens"></param>
  705.         /// <param name="sourceTokens"></param>
  706.         /// <returns></returns>
  707.         private static List<int> DetectContiniousTokens(List<Token> tokens, List<Token> sourceTokens)
  708.         {
  709.             List<int> result = new List<int>();
  710.             if (tokens.Count <= 1)
  711.                 return result;
  712.  
  713.             List<Token> twoTokens = new List<Token>();
  714.             for (int i = 0; i < tokens.Count - 1; i++)
  715.             {
  716.                 if (prepositions.Contains(tokens[i].Word))
  717.                 {
  718.                     result.Add(i);
  719.                     continue;
  720.                 }
  721.  
  722.                 // сравниваем попарно
  723.                 twoTokens.Add(tokens[i]);
  724.                 twoTokens.Add(tokens[i + 1]);
  725.  
  726.                 // склеиваем с разделителями
  727.                 List<Token> allPhraseTokens = GetAllTokensWithSeparators(twoTokens, sourceTokens);
  728.                 string sumWord = TagTokenHelper.RecollectTagsWord(allPhraseTokens);
  729.  
  730.                 // если не содержит пробелов - значит слитно
  731.                 bool containsWhitespace = IsContainsWhitespace(sumWord);
  732.                 if (!containsWhitespace)
  733.                     result.Add(i);
  734.                 twoTokens.Clear();
  735.             }
  736.             return result;
  737.         }
  738.  
  739.         /// <summary>
  740.         /// Собирает токены написанные слитно в диапазоны.
  741.         /// </summary>
  742.         /// <param name="indexes"></param>
  743.         /// <returns></returns>
  744.         private static List<IndexRange> CollectContiniousRanges(List<int> indexes)
  745.         {
  746.             List<IndexRange> result = new List<IndexRange>();
  747.             if (indexes.Count == 0)
  748.                 return result;
  749.  
  750.             int startIdx = indexes[0];
  751.             int len = 1;
  752.             for (int i = 1; i < indexes.Count; i++)
  753.             {
  754.                 if (indexes[i] - len == startIdx)
  755.                     len++;
  756.                 else
  757.                 {
  758.                     result.Add(new IndexRange(startIdx, len + 1));
  759.                     startIdx = indexes[i];
  760.                     len = 1;
  761.                 }
  762.             }
  763.             result.Add(new IndexRange(startIdx, len + 1));
  764.             return result;
  765.         }
  766.  
  767.         /// <summary>
  768.         /// Проверяет содержатся ли пробелы в тексте.
  769.         /// </summary>
  770.         /// <param name="sumWord"></param>
  771.         /// <returns></returns>
  772.         private static bool IsContainsWhitespace(string sumWord)
  773.         {
  774.             foreach (char ch in sumWord)
  775.             {
  776.                 if (char.IsWhiteSpace(ch))
  777.                     return true;
  778.             }
  779.             return false;
  780.         }
  781.  
  782.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement