Repository: nltk/nltk

Issue:
__align and __align_all in IBMModel should be public
The `__align` and `__align_all` functions in `IBMModel`s should should be public. What use is an alignment model if it can only be run on the train set?


Discussion:
It's a little mind-warping but that was the intended use case =)

For the use of machine translations, IBM models are unsupervised and the intended use was to run the models on parallel sentences to learn the alignments and apply them to the same parallel sentences. The inference model should be specific to the data you have trained on. 

Usually, on an unseen data, the model needs to re-learn the alignment weights. 

If we add functionality to infer outside of the training data, it would be good to know instances/usage in publications that trains the alignments and apply it outside of the corpus. Otherwise, we might be adding a functionality that was unintended. 
You might be right in that the original intention was unsupervised learning. However, I don't see why it could not be used to align candidate translations of texts similar to what it was trained on. You could also say that n-gram LMs are trained in an unsupervised fashion, yet they are also used on unseen data. Even from an educational standpoint I would prefer public alignment functions with appropriate disclaimers to private ones.

Also, the API is inconsistent either way, because while `__align()` and `__align_all()` are private, `train()` and `set_uniform_probabilities()` aren't. And `train()` is not incremental, so these functions are really nothing more than implementation details of the constructor.
@hoontw could you help to look into this too?
There is no strong reason to make `__align` functions private. In fact, it used to be public but I changed it in 2ae8dcf3a9b5b86f9bd122fed931a40d8a5e1b88 because it does not work well with unseen data. But the same can be said about other public methods like `prob_t_a_given_s`, so @DavidNemeskey is correct about the inconsistency.

These days, IBM models are not used for translation or obtaining word alignments. The most important artifact is the translation table, which can be used in other machine translation (MT) tasks. So, the IBMModel API is designed as an input/output system:

1. Feed input: `model = IBMModel1(corpus, iterations)`
2. Get output: `translation_table = model.translation_table`. Use translation table in other MT algorithms.
3. Bonus side-effect: alignments are added in the corpus for every sentence pair.

To get alignments for another corpus, create another IBMModel using the translation_table from the previously-trained model: `new_model = IBMModel1(another_corpus, iterations, model.translation_table)`

Note that the usage above involved no functions. In fact, the "public" API can be considered to be the constructor and accessing member variables. Most functions are implementation details. They could be made all private or all public (with the caveat that users mess around with the functions at their own risk).

The upshot is that the `__align` functions can be made public, just to be consistent with the other functions. Alternatively, make everything private.
I think it is more Pythonic (as in "we are all consenting adults") to make them public then to make everything private. Also, less underscores.

Resolving patch:
diff --git a/nltk/translate/ibm1.py b/nltk/translate/ibm1.py
--- a/nltk/translate/ibm1.py
+++ b/nltk/translate/ibm1.py
@@ -139,7 +139,7 @@ def __init__(self, sentence_aligned_corpus, iterations,
         for n in range(0, iterations):
             self.train(sentence_aligned_corpus)
 
-        self.__align_all(sentence_aligned_corpus)
+        self.align_all(sentence_aligned_corpus)
 
     def set_uniform_probabilities(self, sentence_aligned_corpus):
         initial_prob = 1 / len(self.trg_vocab)
@@ -214,11 +214,11 @@ def prob_t_a_given_s(self, alignment_info):
 
         return max(prob, IBMModel.MIN_PROB)
 
-    def __align_all(self, parallel_corpus):
+    def align_all(self, parallel_corpus):
         for sentence_pair in parallel_corpus:
-            self.__align(sentence_pair)
+            self.align(sentence_pair)
 
-    def __align(self, sentence_pair):
+    def align(self, sentence_pair):
         """
         Determines the best word alignment for one sentence pair from
         the corpus that the model was trained on.
diff --git a/nltk/translate/ibm2.py b/nltk/translate/ibm2.py
--- a/nltk/translate/ibm2.py
+++ b/nltk/translate/ibm2.py
@@ -136,7 +136,7 @@ def __init__(self, sentence_aligned_corpus, iterations,
         for n in range(0, iterations):
             self.train(sentence_aligned_corpus)
 
-        self.__align_all(sentence_aligned_corpus)
+        self.align_all(sentence_aligned_corpus)
 
     def set_uniform_probabilities(self, sentence_aligned_corpus):
         # a(i | j,l,m) = 1 / (l+1) for all i, j, l, m
@@ -245,11 +245,11 @@ def prob_t_a_given_s(self, alignment_info):
 
         return max(prob, IBMModel.MIN_PROB)
 
-    def __align_all(self, parallel_corpus):
+    def align_all(self, parallel_corpus):
         for sentence_pair in parallel_corpus:
-            self.__align(sentence_pair)
+            self.align(sentence_pair)
 
-    def __align(self, sentence_pair):
+    def align(self, sentence_pair):
         """
         Determines the best word alignment for one sentence pair from
         the corpus that the model was trained on.


Test patch: