mlpack
bleu_impl.hpp
Go to the documentation of this file.
1 
12 #ifndef MLPACK_CORE_METRICS_BLEU_IMPL_HPP
13 #define MLPACK_CORE_METRICS_BLEU_IMPL_HPP
14 
15 // In case it hasn't been included.
16 #include "bleu.hpp"
17 
18 namespace mlpack {
19 namespace metric {
20 
21 template <typename ElemType, typename PrecisionType>
22 BLEU<ElemType, PrecisionType>::BLEU(const size_t maxOrder) :
23  maxOrder(maxOrder),
24  translationLength(0),
25  referenceLength(0)
26 {
27  // Nothing to do here.
28 }
29 
30 template <typename ElemType, typename PrecisionType>
31 template <typename WordVector>
32 std::map<WordVector, size_t> BLEU<ElemType, PrecisionType>::GetNGrams(
33  const WordVector& segment)
34 {
35  std::map<WordVector, size_t> ngramsCount;
36  for (size_t order = 1; order < maxOrder + 1; ++order)
37  {
38  for (size_t i = 0; i + order < segment.size() + 1; ++i)
39  {
40  WordVector seq = WordVector(segment.cbegin() + i,
41  segment.cbegin() + i + order);
42  ngramsCount[seq]++;
43  }
44  }
45  return ngramsCount;
46 }
47 
48 template <typename ElemType, typename PrecisionType>
49 template <typename ReferenceCorpusType, typename TranslationCorpusType>
51  const ReferenceCorpusType& referenceCorpus,
52  const TranslationCorpusType& translationCorpus,
53  const bool smooth)
54 {
55  // WordVector is a string container type.
56  // Also, TranslationCorpusType is an array of such containers.
57  typedef typename TranslationCorpusType::value_type WordVector;
58 
59  // matchesByOrder: It catches how many times sequence of a particular order
60  // is encountered in both reference corpus and translation corpus.
61  std::vector<size_t> matchesByOrder(maxOrder, 0);
62 
63  // possibleMatchesByOrder: It tracks how many possible matches can be in the
64  // translation corpus.
65  std::vector<size_t> possibleMatchesByOrder(maxOrder, 0);
66 
67  // referenceLength: It is the sum of minimum length of the paragraph from
68  // various documents.
69  // translationLength: It is the sum of length of each paragraphs.
70  referenceLength = 0, translationLength = 0;
71 
72  auto refIt = referenceCorpus.cbegin();
73  auto trIt = translationCorpus.cbegin();
74  for (; refIt != referenceCorpus.cend() && trIt != translationCorpus.cend();
75  ++refIt, ++trIt)
76  {
77  size_t min = std::numeric_limits<size_t>::max();
78  for (const auto& t : *refIt)
79  {
80  if (min > t.size())
81  {
82  min = t.size();
83  }
84  }
85 
86  if (min == std::numeric_limits<size_t>::max())
87  min = 0;
88 
89  referenceLength += min;
90  translationLength += trIt->size();
91 
92  // mergedRefNGramCounts: It accumulates all the similar n-grams from
93  // various references or documents, so that there is no repetition of
94  // any key (sequence of order n).
95  std::map<WordVector, size_t> mergedRefNGramCounts;
96  for (const auto& t : *refIt)
97  {
98  // ngram: It holds the n-grams of each document/reference.
99  const std::map<WordVector, size_t> ngrams = GetNGrams(t);
100  for (auto it = ngrams.cbegin(); it != ngrams.cend(); ++it)
101  {
102  mergedRefNGramCounts[it->first] = std::max(it->second,
103  mergedRefNGramCounts[it->first]);
104  }
105  }
106  // translationNGramCounts: It extracts the n-grams of the generated text
107  // sequence.
108  const std::map<WordVector, size_t> translationNGramCounts
109  = GetNGrams(*trIt);
110 
111  // overlap: It holds those keys (sequence of order n) which are common to
112  // reference corpus and translation corpus.
113  std::map<WordVector, size_t> overlap;
114  for (auto it = translationNGramCounts.cbegin();
115  it != translationNGramCounts.cend();
116  ++it)
117  {
118  auto mergedIt = mergedRefNGramCounts.find(it->first);
119  if (mergedIt != mergedRefNGramCounts.end())
120  {
121  // If the key (sequence of order n) is present in both translation
122  // corpus as well as reference corpus, then the minimum number of
123  // counts it has occurred in any is considered.
124  overlap[it->first] = std::min(mergedIt->second, it->second);
125  }
126  }
127 
128  for (auto it = overlap.cbegin(); it != overlap.cend(); ++it)
129  {
130  matchesByOrder[it->first.size() - 1] += it->second;
131  }
132 
133  for (size_t order = 1; order < maxOrder + 1; ++order)
134  {
135  if (order < trIt->size() + 1)
136  possibleMatchesByOrder[order - 1] += trIt->size() - order + 1;
137  }
138  }
139 
140  precisions = PrecisionType(maxOrder, 0.0);
141 
142  if (smooth)
143  {
144  for (size_t i = 0; i < maxOrder; ++i)
145  {
146  precisions[i]
147  = (matchesByOrder[i] + 1.0) / (possibleMatchesByOrder[i] + 1.0);
148  }
149  }
150  else
151  {
152  for (size_t i = 0; i < maxOrder; ++i)
153  {
154  if (possibleMatchesByOrder[i] > 0)
155  precisions[i] = ElemType(matchesByOrder[i]) / possibleMatchesByOrder[i];
156  else
157  precisions[i] = 0.0;
158  }
159  }
160 
161  ElemType minPrecision = std::numeric_limits<ElemType>::max();
162  for (size_t i = 0; i < maxOrder; ++i)
163  {
164  if (minPrecision > precisions[i])
165  minPrecision = precisions[i];
166  }
167 
168  ElemType geometricMean;
169  if (minPrecision > 0)
170  {
171  ElemType pLogSum = 0.0;
172  for (const auto& t : precisions)
173  {
174  pLogSum += (1.0 / maxOrder) * std::log(t);
175  }
176  geometricMean = std::exp(pLogSum);
177  }
178  else
179  geometricMean = 0.0;
180 
181  ratio = ElemType(translationLength);
182  if (referenceLength > 0)
183  ratio /= referenceLength;
184 
185  brevityPenalty = (ratio > 1.0) ? 1.0 : std::exp(1.0 - 1.0 / ratio);
186  bleuScore = geometricMean * brevityPenalty;
187 
188  return bleuScore;
189 }
190 
191 template <typename ElemType, typename PrecisionType>
192 template <typename Archive>
194  const uint32_t version)
195 {
196  ar(CEREAL_NVP(maxOrder));
197 }
198 
199 } // namespace metric
200 } // namespace mlpack
201 
202 #endif
BLEU(const size_t maxOrder=4)
Create an instance of BLEU class.
Definition: bleu_impl.hpp:22
Linear algebra utility functions, generally performed on matrices or vectors.
Definition: cv.hpp:1
void serialize(Archive &ar, const uint32_t version)
Serialize the metric.
Definition: bleu_impl.hpp:193
ElemType Evaluate(const ReferenceCorpusType &referenceCorpus, const TranslationCorpusType &translationCorpus, const bool smooth=false)
Computes the BLEU Score.
Definition: bleu_impl.hpp:50
BLEU, or the Bilingual Evaluation Understudy, is an algorithm for evaluating the quality of text whic...
Definition: bleu.hpp:53