EI328 Final Project Review (II)

 

  A further step of this project is to implement Min-Max Modular Neural Network with parallel computing, which makes use of Liblinear library as the basic classifier.

  We use Java multi-threading to solve this problem, and create a class called MinMax (./src/MinMax.java) to accomplish task 2 and task 3. At first, the main thread (a static method called by main) generates all the working threads (MinMax objects) after some requisite initializations. Then main_thread decomposes sub-problems (either randomly in task 2, or in light of prior knowledge in task 3) and puts them into a BlockingQueue called probBuf, while the working threads at the same time take sub-problems from probBuf, use Liblinear to generate models and put models into another queue called modBuf. After main_thread has provided all the sub-problems (as well as a sentinel), it will turn to modBuf, process the models generated by working threads (modify the thresholds) and put new models into another queue called newModBuf. When all the working threads have finished training, they will release waitTask signals to main_thread, and when main_thread gets all such signals it will give its order by releasing another signals called nextTask. In this way, testing procedure starts, and working threads repeatedly extract models from newModBuf and repeatedly revise minResult until newModBuf is empty. After that, main_thread will do the "maximization" to consummate the classification.

 

  1 /**
  2 * Argument Format:
  3 * <task # = 2 or 3> <number of threads> <min group size> <max group size> <group size step> 
  4 *                <min threshold> <max threshold> <threshold step>
  5 **/
  6 
  7 import de.bwaldvogel.liblinear.*;
  8 import java.util.concurrent.atomic.*;
  9 import java.util.concurrent.*;
 10 import java.util.*;
 11 import java.io.*;
 12 
 13 class Subprob {
 14     private List<Integer> data;
 15     private Integer minIndex;
 16     
 17     public Subprob(int idx) {
 18         minIndex = new Integer(idx);
 19         data = new LinkedList<Integer>();
 20     }
 21     public void add(Integer item) {
 22         data.add(item);
 23     }
 24     public int size() {
 25         return data.size();
 26     }
 27     public Integer getItem(int idx) {
 28         if (idx<0||idx>=data.size()) {
 29             throw new RuntimeException("Subprob: IndexOutOfBound");
 30         }
 31         return data.get(idx);
 32     }
 33     public Integer getIndex() {
 34         return minIndex;
 35     }
 36 }
 37 
 38 class DataItem implements Comparable<DataItem> {
 39     private Integer index;
 40     private String info;
 41     
 42     public DataItem(int idx,String info) {
 43         index = new Integer(idx);
 44         this.info = info;
 45     }
 46     public int compareTo(DataItem other) {
 47         return info.compareTo(other.info);
 48     }
 49     public Integer getValue() {
 50         return index;
 51     }
 52 }
 53 
 54 public class MinMax extends AbstractTask {
 55     private static int NUM;
 56     private static int NUM_OF_THREAD;
 57     
 58     private static Semaphore nextTask;
 59     private static Semaphore waitTask;
 60     private static Semaphore timeMutex;
 61     private static Subprob probSentinel;
 62     private static Model modSentinel;
 63     
 64     private static BlockingQueue<Subprob> probBuf;
 65     private static BlockingQueue<Model> modBuf;
 66     private static BlockingQueue<Model> newModBuf;
 67     private static BlockingQueue<Integer> minIdx;
 68     
 69     private static AtomicInteger[][] minResult;
 70     
 71     static {
 72         try {
 73             init();
 74             nextTask = new Semaphore(0);
 75             waitTask = new Semaphore(0);
 76             timeMutex = new Semaphore(1);
 77             probSentinel = new Subprob(-1);
 78             modSentinel = new Model();
 79             probBuf = new LinkedBlockingQueue<Subprob>();
 80             modBuf = new LinkedBlockingQueue<Model>();
 81             newModBuf = new LinkedBlockingQueue<Model>();
 82             minIdx = new LinkedBlockingQueue<Integer>();
 83         } catch (Exception e) {
 84             System.err.println("STATIC Error: "+e);
 85         }
 86     }
 87     private static void separate(List<Integer> poslst,List<Integer> neglst,boolean prior) {
 88         // List the positive samples and negative samples:
 89         // Precondition: poslst and neglst are non-null empty lists
 90         // Postcondition: poslst and neglst are filled with indices of positive
 91         //            and negative training data respectively
 92         if (!prior) {
 93             for (int i=0;i<train.l;i++) {
 94                 if (train.y[i]>0) {
 95                     poslst.add(new Integer(i));
 96                 } else {
 97                     neglst.add(new Integer(i));
 98                 }
 99             }
100             Random rand = new Random();
101             Collections.shuffle(poslst,rand);
102             Collections.shuffle(neglst,rand);
103         } else {
104             try {
105                 System.out.println("Gathering Prior Knowledge ... ");
106                 BufferedReader pin = new BufferedReader(new FileReader("./data/train.txt"));
107                 List<DataItem> posData = new LinkedList<DataItem>();
108                 List<DataItem> negData = new LinkedList<DataItem>();
109                 for (int i=0;i<train.l;i++) {
110                     String line = pin.readLine();
111                     if (train.y[i]>0) {
112                         posData.add(new DataItem(i,line.substring(0,3)));
113                     } else {
114                         negData.add(new DataItem(i,line.substring(0,3)));
115                     }
116                 }
117                 pin.close();
118                 //System.in.read();
119                 Collections.sort(posData);
120                 Collections.sort(negData);
121                 for (int i=0;i<posData.size();i++) {
122                     poslst.add(posData.get(i).getValue());
123                 }
124                 for (int i=0;i<negData.size();i++) {
125                     neglst.add(negData.get(i).getValue());
126                 }
127             } catch (Exception e) {
128                 System.err.println("SEPARATE Error: "+e);
129             }
130         }
131     }
132     private static void distribute(boolean prior) {
133         List<Integer> poslst = new LinkedList<Integer>();
134         List<Integer> neglst = new LinkedList<Integer>();
135         separate(poslst,neglst,prior);
136         // Group the positive samples and negative samples:
137         int posGrpNum = (poslst.size()+NUM-1)/NUM;
138         int negGrpNum = (neglst.size()+NUM-1)/NUM;
139         int[] posGrps = new int[posGrpNum+1];
140         int[] negGrps = new int[negGrpNum+1];
141         for (int i=0;i<posGrpNum;i++) {
142             posGrps[i+1] = posGrps[i]+(poslst.size()+i)/posGrpNum;
143         }
144         for (int i=0;i<negGrpNum;i++) {
145             negGrps[i+1] = negGrps[i]+(neglst.size()+i)/negGrpNum;
146         }
147         try {    // Add tasks to the buffer:
148             System.out.println("Distribute subproblems ... ");
149             start = System.currentTimeMillis();        // start training
150             for (int i=0;i<posGrpNum;i++) {
151                 for (int j=0;j<negGrpNum;j++) {
152                     Subprob sub = new Subprob(i);
153                     for (int k=posGrps[i];k<posGrps[i+1];k++) {
154                         sub.add(poslst.get(k));
155                     }
156                     for (int k=negGrps[j];k<negGrps[j+1];k++) {
157                         sub.add(neglst.get(k));
158                     }
159                     probBuf.put(sub);
160                 }
161             }
162         } catch (Exception e) {
163             System.err.println("DISTRIBUTE Error 1: "+e);
164         }
165         try {    // Prepare for the MIN modules:
166             minResult = new AtomicInteger[posGrpNum][test.l];
167             for (int i=0;i<posGrpNum;i++) {
168                 for (int j=0;j<test.l;j++) {
169                     minResult[i][j] = new AtomicInteger(1);
170                 }
171             }
172             probBuf.put(probSentinel);    // "STOP TRAINING" signal
173         } catch (Exception e) {
174             System.err.println("DISTRIBUTE Error 2: "+e);
175         }
176     }
177     private static void setMins(double threshold) {
178         int cnt = 0;
179         Model model = null;
180         try {
181             System.out.println("Preparations for MIN modules ...");
182             while (cnt<NUM_OF_THREAD) {
183                 model = modBuf.take();
184                 if (model==modSentinel) {
185                     cnt++;
186                     continue;
187                 }
188                 model = modModify(model,threshold);
189                 newModBuf.put(model);
190             }
191             newModBuf.put(modSentinel);    // "STOP TESTING" signal
192         } catch (Exception e) {
193             System.err.println("SETMINS Error: "+e);
194         }
195     }
196     private static void main_thread(double threshold,boolean prior) {
197         try {
198             MinMax[] tsks = new MinMax[NUM_OF_THREAD];
199             //distribute(prior);    start = System.currentTimeMillis();    // for task 5
200             end = -1;
201             for (int i=0;i<NUM_OF_THREAD;i++) {
202                 tsks[i] = new MinMax();
203                 tsks[i].start();
204             }
205             distribute(prior);
206             setMins(threshold);
207             for (int i=0;i<NUM_OF_THREAD;i++) {
208                 waitTask.acquire();                    // training finished
209             }
210             for (int i=0;i<NUM_OF_THREAD;i++) {
211                 nextTask.release();                    // testing enabled
212             }
213             start = System.currentTimeMillis();        // start testing
214             for (int i=0;i<NUM_OF_THREAD;i++) {
215                 tsks[i].join();
216             }
217             int[] res = new int[test.l];
218             Arrays.fill(res,-1);
219             for (int i=0;i<minResult.length;i++) {
220                 for (int j=0;j<test.l;j++) {
221                     if (minResult[i][j].get()>0) {
222                         res[j] = 1;                    // MAX Module
223                     }
224                 }
225             }
226             end = System.currentTimeMillis();        // finish testing
227             printTime(end-start,false);
228             stats(res);
229         } catch (Exception e) {
230             System.err.println("MAIN_THRAD Error: "+e);
231         }
232     }
233     public static void main(String[] args) {
234         try {
235             out = new PrintWriter(new FileWriter("./result/task"+args[0]+"_result.out"));
236             NUM_OF_THREAD = Integer.parseInt(args[1]);
237             int numMin = Integer.parseInt(args[2]);
238             int numMax = Integer.parseInt(args[3]);
239             int numStep = Integer.parseInt(args[4]);
240             double tmin = Double.parseDouble(args[5]);
241             double tmax = Double.parseDouble(args[6]);
242             double tstep = Double.parseDouble(args[7]);
243             for (NUM=numMin;NUM<=numMax;NUM+=numStep) {
244                 for (double t=tmin;t<tmax+tstep/2;t+=tstep) {
245                     print("Group Size = "+NUM+",\t");
246                     print(NUM_OF_THREAD+" Threads,\t");
247                     println("Threshold = "+t);
248                     probBuf.clear();
249                     modBuf.clear();
250                     newModBuf.clear();
251                     minIdx.clear();
252                     if (args[0].equals("2")) {
253                         main_thread(t,false);
254                     } else {
255                         main_thread(t,true);
256                     }
257                 }
258                 rocWrtLine("_next_");
259             }
260             rocWrtLine("_exit_");
261             out.close();
262         } catch (Exception e) {
263             System.err.println("MAIN Error: "+e);
264         }
265     }
266     
267     public void run() {
268         try {
269             train();
270             waitTask.release();
271             nextTask.acquire();
272             test();
273         } catch (Exception e) {
274             System.err.println("RUN Error: "+e);
275         }
276     }
277     private void train() {
278         Subprob sub = null;
279         try {
280             while (true) {
281                 sub = probBuf.take();
282                 if (sub==probSentinel) {    // signal of termination
283                     timeMutex.acquire();
284                     if (end<0) {            // finish training
285                         end = System.currentTimeMillis();
286                         printTime(end-start,true);
287                     }
288                     timeMutex.release();
289                     probBuf.put(sub);
290                     break;
291                 }
292                 Problem prob = new Problem();
293                 prob.l = sub.size();
294                 prob.n = train.n;
295                 prob.x = new Feature[prob.l][];
296                 prob.y = new double[prob.l];
297                 prob.bias = 1;
298                 for (int i=0;i<sub.size();i++) {
299                     int pos = sub.getItem(i).intValue();
300                     prob.x[i] = train.x[pos];
301                     prob.y[i] = train.y[pos];
302                 }
303                 modBuf.put(Linear.train(prob,param));
304                 minIdx.put(sub.getIndex());
305             }
306             modBuf.put(modSentinel);
307         } catch (Exception e) {
308             System.err.println("TRAIN Error: "+e);
309         }
310     }
311     private void test() {
312         Model model = null;
313         int idx = -1;
314         try {
315             while (true) {
316                 model = newModBuf.take();
317                 if (model==modSentinel) {    // signal of termination
318                     newModBuf.put(model);
319                     break;
320                 }
321                 idx = minIdx.poll().intValue();
322                 for (int i=0;i<test.l;i++) {
323                     if (Linear.predict(model,test.x[i])<0) {
324                         minResult[idx][i].getAndSet(-1);    // MIN Modules
325                     }
326                 }
327             }
328         } catch (Exception e) {
329             System.err.println("TEST Error:"+e);
330         }
331     }
332 }

 

  The distribute method in the MinMax class above exploits an average distribution technique. We use a parameter called NUM to balance the both positive and negative samples in a training sub-problem.

  At first, NUM will determine the number of positive groups and negative groups:

      $n_1=\lceil N_1/NUM\rceil$  and  $n_2=\lceil N_2/NUM\rceil$, 

where $N_1$ and $N_2$ are the numbers of positive and negative samples respectively.

  Then, we distribute to $n_1$ positive groups and $n_2$ negative groups approximately equal samples by using the following equation:

      $N=\sum_{i=0}^{n-1}\lfloor (N+i)/n\rfloor$

where we let $N=N_1, n=n_1$ and $N=N_2, n=n_2$ respectively.

  For any positive group (j=0) or negative group (j=1), the upper bound of sample number is:

      $\lceil \frac{N_j}{\lceil N_j/NUM\rceil}\rceil< \frac{N_j}{\lceil N_j/NUM\rceil}+1\leq NUM+1$

and the lower bound of sample number will be:

      $\lfloor \frac{N_j}{\lceil N_j/NUM\rceil}\rfloor> \frac{N_j}{\lceil N_j/NUM\rceil}-1> (\frac{1}{N_j}+\frac{1}{NUM})^{-1}-1$

  Thus, suppose $N_j\leq N_{1-j}$, when $1\ll NUM\ll N_j$ holds, the ratio of two kinds of samples in a group will be:

      $\frac{({N_j}^{-1}+NUM^{-1})^{-1}-1}{NUM+1}\approx \frac{NUM-1}{NUM+1}\approx 1$

 

  In task 5, we are required to measure the time performance of this program when prior knowledge is used. Here we wrote two python scripts to collect data and finally plot line charts to show both the training time and testing time in different NUM_OF_THREADs.

  This is ./src/timeHelp, which collects data from ./result/task3_result.out:

 1 #! /usr/bin/python
 2 import sys
 3 fin = open("./result/task3_result.out","r")
 4 fout = open("./result/task3_time.txt","a")
 5 # number of threads
 6 fout.write(sys.argv[1]+' ')
 7 # training time
 8 fin.readline()
 9 tmp = fin.readline().split("\t")
10 tok = tmp[2].split('ms')
11 fout.write(tok[0]+' ')
12 # testing time
13 tmp = fin.readline().split('\t')
14 tok = tmp[2].split('ms')
15 fout.write(tok[0]+'\n')
16 fin.close()
17 fout.close()

  And this is the source code of ./src/timePlot. which plots two line charts:

 1 #! /usr/bin/python
 2 import pylab as pl
 3 import numpy as np
 4 data = np.loadtxt("./result/task3_time.txt");
 5 num = data[:,0]
 6 ltrain = [1626]*len(num)
 7 train = data[:,1]
 8 ltest = [41]*len(num)
 9 test = data[:,2]
10 fg1 = pl.figure()
11 plot1, = pl.plot(num,ltrain)
12 plot2, = pl.plot(num,train)
13 pl.title('Training Time vs NUM_OF_THREAD')
14 pl.xlabel('NUM_OF_THREAD')
15 pl.ylabel('Training Time')
16 pl.legend((plot1,plot2),('LIBLINEAR','Min-Max Module'),'best',numpoints=1)
17 fg2 = pl.figure()
18 plot3, = pl.plot(num,ltest)
19 plot4, = pl.plot(num,test)
20 pl.title('Testing Time vs NUM_OF_THREAD')
21 pl.xlabel('NUM_OF_THREAD')
22 pl.ylabel('Testing Time')
23 pl.legend((plot3,plot4),('LIBLINEAR','Min-Max Module'),'best',numpoints=1)
24 pl.show()

 

  We wrote a shell script to run the programs above and obtained two line charts:

 1 #! /bin/bash
 2 reset
 3 make
 4 for var in 1 2 3 4 5 6 7 8 9 10 15 20 25 30 35 40
 5 do
 6     java -classpath ./bin/:./bin/liblinear-java-1.95.jar MinMax 3 $var 8000 8000 1000 -0.00 0.00 0.01
 7     python ./src/timeHelp $var
 8 done
 9 
10 python ./src/timePlot

 

 

posted on 2015-05-20 10:42  DevinZ  阅读(266)  评论(0编辑  收藏  举报

导航