praat_contrib_Ola_KNN.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. /* praat_contrib_Ola_KNN.cpp
  2. *
  3. * Copyright (C) 2007-2009 Ola Söder, 2010-2012,2015-2018 Paul Boersma
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /*
  19. * os 2007/05/29 Initial release?
  20. * os 2009/01/23 Bugfix: Removed MUX:ing (KNN_learn) incompatible with the scripting engine. Thanks to Paul Boersma for spotting this problem.
  21. * pb 2010/12/28 in messages: typos, English, interpunction
  22. * pb 2011/07/12 C++ and removed several errors
  23. * pb 2011/09/18 made a start with handling dataChanged at all
  24. * pb 2016/10/19 loops, dataChanged, Praat idiom for names, English
  25. */
  26. #include "KNN.h"
  27. #include "KNN_threads.h"
  28. #include "KNN_prune.h"
  29. #include "Pattern_to_Categories_cluster.h"
  30. #include "FeatureWeights.h"
  31. #include "praat_FFNet.h"
  32. #undef iam
  33. #define iam iam_LOOP
  34. static const conststring32 QUERY_BUTTON = U"Query -";
  35. static const conststring32 MODIFY_BUTTON = U"Modify -";
  36. static const conststring32 EXTRACT_BUTTON = U"Extract -";
  37. /////////////////////////////////////////////////////////////////////////////////////////
  38. // KNN creations //
  39. /////////////////////////////////////////////////////////////////////////////////////////
  40. FORM (NEW1_KNN_create, U"Create kNN Classifier", U"kNN classifiers 1. What is a kNN classifier?") {
  41. WORD (name, U"Name", U"Classifier")
  42. OK
  43. DO
  44. CREATE_ONE
  45. autoKNN result = KNN_create ();
  46. CREATE_ONE_END (name)
  47. }
  48. FORM (NEW1_PatternList_Categories_to_KNN, U"Create kNN classifier", U"kNN classifiers 1. What is a kNN classifier?") {
  49. WORD (name, U"Name", U"Classifier")
  50. RADIOx (ordering, U"Ordering", 1, 1)
  51. RADIOBUTTON (U"Random")
  52. RADIOBUTTON (U"Sequential")
  53. OK
  54. DO
  55. CONVERT_TWO (PatternList, Categories)
  56. autoKNN result = KNN_create ();
  57. switch (ordering) {
  58. case 1:
  59. ordering = kOla_SHUFFLE;
  60. break;
  61. case 2:
  62. ordering = kOla_SEQUENTIAL;
  63. }
  64. int status = KNN_learn (result.get(), me, you, kOla_REPLACE, ordering);
  65. if (status == kOla_PATTERN_CATEGORIES_MISMATCH)
  66. Melder_throw (U"The number of Categories should be equal to the number of rows in PatternList.");
  67. if (status == kOla_DIMENSIONALITY_MISMATCH)
  68. Melder_throw (U"The dimensionality of PatternList should be equal to that of the instance base.");
  69. CONVERT_TWO_END (name)
  70. }
  71. /////////////////////////////////////////////////////////////////////////////////////////
  72. // KNN extractions, queries and modifications //
  73. /////////////////////////////////////////////////////////////////////////////////////////
  74. DIRECT (INTEGER_KNN_getNumberOfInstances) {
  75. NUMBER_ONE (KNN)
  76. integer result = my nInstances;
  77. NUMBER_ONE_END (U" units")
  78. }
  79. FORM (INTEGER_KNN_getOptimumModel, U"kNN model selection", U"kNN classifiers 1.1.2. Model selection") {
  80. RADIOx (evaluationMethod, U"Evaluation method", 1, 1)
  81. RADIOBUTTON (U"Leave one out")
  82. RADIOBUTTON (U"10-fold cross-validation")
  83. INTEGER (kmax, U"k max", U"50")
  84. INTEGER (numberOfSeeds, U"Number of seeds", U"10")
  85. POSITIVE (learningRate, U"Learning rate", U"0.2")
  86. OK
  87. DO
  88. INFO_ONE (KNN)
  89. if (kmax < 1 || kmax > my nInstances)
  90. Melder_throw (U"Please select a value of k max such that 0 < k max < ", my nInstances + 1, U".");
  91. if (numberOfSeeds < 1)
  92. Melder_throw (U"The number of seeds should exceed 1."); // BUG
  93. switch (evaluationMethod) {
  94. case 1:
  95. evaluationMethod = kOla_LEAVE_ONE_OUT;
  96. break;
  97. case 2:
  98. evaluationMethod = kOla_TEN_FOLD_CROSS_VALIDATION;
  99. break;
  100. }
  101. autoFeatureWeights fws = FeatureWeights_create (my input -> nx);
  102. int dist;
  103. KNN_modelSearch (me, fws.get(), & kmax, & dist, evaluationMethod, learningRate, numberOfSeeds);
  104. switch (dist) {
  105. case kOla_SQUARED_DISTANCE_WEIGHTED_VOTING:
  106. Melder_information (kmax, U" (vote weighting: inverse squared distance)");
  107. break;
  108. case kOla_DISTANCE_WEIGHTED_VOTING:
  109. Melder_information (kmax, U" (vote weighting: inverse distance)");
  110. break;
  111. case kOla_FLAT_VOTING:
  112. Melder_information (kmax, U" (vote weighting: flat)");
  113. break;
  114. }
  115. INFO_ONE_END
  116. }
  117. FORM (REAL_KNN_evaluate, U"Evaluation", U"KNN: Get accuracy estimate...") {
  118. RADIOx (evaluationMethod, U"Evaluation method", 1, 1)
  119. RADIOBUTTON (U"Leave one out")
  120. RADIOBUTTON (U"10-fold cross-validation")
  121. INTEGER (kNeighbours, U"k neighbours", U"1")
  122. RADIOx (voteWeighting, U"Vote weighting", 1, 1)
  123. RADIOBUTTON (U"Inverse squared distance")
  124. RADIOBUTTON (U"Inverse distance")
  125. RADIOBUTTON (U"Flat")
  126. OK
  127. DO
  128. FIND_ONE (KNN)
  129. if (my nInstances < 1)
  130. Melder_throw (U"Instance base is empty.");
  131. if (kNeighbours < 1 || kNeighbours > my nInstances)
  132. Melder_throw (U"Please select a value of k such that 0 < k < ", my nInstances + 1, U".");
  133. switch (voteWeighting) {
  134. case 1:
  135. voteWeighting = kOla_SQUARED_DISTANCE_WEIGHTED_VOTING;
  136. break;
  137. case 2:
  138. voteWeighting = kOla_DISTANCE_WEIGHTED_VOTING;
  139. break;
  140. case 3:
  141. voteWeighting = kOla_FLAT_VOTING;
  142. break;
  143. }
  144. switch (evaluationMethod) {
  145. case 2:
  146. evaluationMethod = kOla_TEN_FOLD_CROSS_VALIDATION;
  147. break;
  148. case 1:
  149. evaluationMethod = kOla_LEAVE_ONE_OUT;
  150. break;
  151. }
  152. autoFeatureWeights fws = FeatureWeights_create (my input -> nx);
  153. double result = KNN_evaluate (me, fws.get(), kNeighbours, voteWeighting, evaluationMethod);
  154. if (Melder_iround (result) == kOla_FWEIGHTS_MISMATCH)
  155. Melder_throw (U"The number of feature weights should be equal to the dimensionality of the PatternList.");
  156. Melder_information (100 * result, U" percent of the instances correctly classified."); // BUG: use Melder_percent
  157. END
  158. }
  159. FORM (REAL_KNN_FeatureWeights_evaluate, U"Evaluation", U"KNN & FeatureWeights: Get accuracy estimate...") {
  160. RADIOx (evaluationMethod, U"Evaluation method", 1, 1)
  161. RADIOBUTTON (U"Leave one out")
  162. RADIOBUTTON (U"10-fold cross-validation")
  163. INTEGER (kNeighbours, U"k neighbours", U"1")
  164. RADIOx (voteWeighting, U"Vote weighting", 1, 1)
  165. RADIOBUTTON (U"Inverse squared distance")
  166. RADIOBUTTON (U"Inverse distance")
  167. RADIOBUTTON (U"Flat")
  168. OK
  169. DO
  170. FIND_TWO (KNN, FeatureWeights)
  171. if (my nInstances < 1)
  172. Melder_throw (U"Instance base is empty");
  173. if (kNeighbours < 1 || kNeighbours > my nInstances)
  174. Melder_throw (U"Please select a value of k such that 0 < k < ", my nInstances + 1, U".");
  175. switch (voteWeighting) {
  176. case 1:
  177. voteWeighting = kOla_SQUARED_DISTANCE_WEIGHTED_VOTING;
  178. break;
  179. case 2:
  180. voteWeighting = kOla_DISTANCE_WEIGHTED_VOTING;
  181. break;
  182. case 3:
  183. voteWeighting = kOla_FLAT_VOTING;
  184. break;
  185. }
  186. switch (evaluationMethod) {
  187. case 2:
  188. evaluationMethod = kOla_TEN_FOLD_CROSS_VALIDATION;
  189. break;
  190. case 1:
  191. evaluationMethod = kOla_LEAVE_ONE_OUT;
  192. break;
  193. }
  194. double result = KNN_evaluate (me, you, kNeighbours, voteWeighting, evaluationMethod);
  195. if (Melder_iround (result) == kOla_FWEIGHTS_MISMATCH)
  196. Melder_throw (U"The number of feature weights should be equal to the dimensionality of the PatternList.");
  197. Melder_information (100 * result, U" percent of the instances correctly classified."); // BUG: never report a percentage; always report a fraction
  198. END
  199. }
  200. DIRECT (NEW_KNN_extractInputPatterns) {
  201. CONVERT_EACH (KNN)
  202. if (my nInstances <= 0)
  203. Melder_throw (U"Instance base is empty.");
  204. autoPatternList result = Data_copy (my input.get());
  205. CONVERT_EACH_END (my name.get(), U"_input")
  206. }
  207. DIRECT (NEW_KNN_extractOutputCategories) {
  208. CONVERT_EACH (KNN)
  209. if (my nInstances <= 0)
  210. Melder_throw (U"Instance base is empty.");
  211. autoCategories result = Data_copy (my output.get());
  212. CONVERT_EACH_END (my name.get(), U"_output");
  213. }
  214. FORM (MODIFY_KNN_reset, U"Reset", U"KNN: Reset...") {
  215. LABEL (U"Warning: this command destroys all previous learning.")
  216. OK
  217. DO
  218. MODIFY_EACH (KNN)
  219. my input.reset();
  220. my output.reset();
  221. my nInstances = 0;
  222. MODIFY_EACH_END
  223. }
  224. DIRECT (MODIFY_KNN_shuffle) {
  225. MODIFY_EACH (KNN)
  226. if (my nInstances <= 0)
  227. Melder_throw (U"Instance base is empty.");
  228. KNN_shuffleInstances (me);
  229. praat_dataChanged (me);
  230. MODIFY_EACH_END
  231. }
  232. FORM (INFO_MODIFY_KNN_prune, U"Pruning", U"KNN: Prune...") {
  233. POSITIVE (noisePruningDegree, U"Noise pruning degree", U"1")
  234. POSITIVE (redundancyPruningDegree, U"Redundancy pruning degree", U"1")
  235. INTEGER (kNeighbours, U"k neighbours", U"1")
  236. OK
  237. DO
  238. FIND_ONE (KNN)
  239. if (my nInstances <= 0)
  240. Melder_throw (U"Instance base is empty.");
  241. integer oldn = my nInstances; // save before it changes!
  242. if (kNeighbours < 1 || kNeighbours > my nInstances)
  243. Melder_throw (U"Please select a value of k such that 0 < k < ", my nInstances + 1, U".");
  244. if (noisePruningDegree <= 0.0 || noisePruningDegree > 1.0)
  245. Melder_throw (U"The noise pruning degree should be between 0.0 (excluded) and 1.0 (included).");
  246. if (redundancyPruningDegree <= 0.0 || redundancyPruningDegree > 1.0)
  247. Melder_throw (U"The redundancy pruning degree should be between 0.0 (excluded) and 1.0 (included).");
  248. integer npruned = KNN_prune_prune (me, noisePruningDegree, redundancyPruningDegree, kNeighbours); // BUG: the KNN is changed
  249. Melder_information (npruned, U" instances discarded. \n", U"Size of new instance base: ", oldn - npruned);
  250. END
  251. }
  252. /////////////////////////////////////////////////////////////////////////////////////////
  253. // Learning //
  254. /////////////////////////////////////////////////////////////////////////////////////////
  255. FORM (MODIFY_KNN_PatternList_Categories_learn, U"Learning", U"kNN classifiers 1. What is a kNN classifier?") {
  256. RADIOx (learningMethod, U"Learning method", 1, 1)
  257. RADIOBUTTON (U"Append new information")
  258. RADIOBUTTON (U"Replace current instance base")
  259. RADIOx (ordering, U"Ordering", 1, 1)
  260. RADIOBUTTON (U"Random")
  261. RADIOBUTTON (U"Sequential")
  262. OK
  263. DO
  264. FIND_THREE (KNN, PatternList, Categories)
  265. switch (ordering) {
  266. case 1:
  267. ordering = kOla_SHUFFLE;
  268. break;
  269. case 2:
  270. ordering = kOla_SEQUENTIAL;
  271. }
  272. int result = kOla_ERROR;
  273. switch (learningMethod) {
  274. case 1:
  275. result = KNN_learn (me, you, him, my nInstances == 0 ? kOla_REPLACE : kOla_APPEND, ordering);
  276. break;
  277. case 2:
  278. result = KNN_learn (me, you, him, kOla_REPLACE, ordering);
  279. break;
  280. }
  281. switch (result) {
  282. case kOla_PATTERN_CATEGORIES_MISMATCH:
  283. Melder_throw (U"The number of Categories should be equal to the number of rows in PatternList.");
  284. case kOla_DIMENSIONALITY_MISMATCH:
  285. Melder_throw (U"The dimensionality of PatternList should be equal to that of the instance base.");
  286. }
  287. END
  288. }
  289. /////////////////////////////////////////////////////////////////////////////////////////
  290. // Evaluation //
  291. /////////////////////////////////////////////////////////////////////////////////////////
  292. FORM (BUG_KNN_evaluateWithTestSet, U"Evaluation", U"KNN & PatternList & Categories: Evaluate...") {
  293. INTEGER (kNeighbours, U"k neighbours", U"1")
  294. RADIOx (voteWeighting, U"Vote weighting", 1, 1)
  295. RADIOBUTTON (U"Inverse squared distance")
  296. RADIOBUTTON (U"Inverse distance")
  297. RADIOBUTTON (U"Flat")
  298. OK
  299. DO
  300. FIND_THREE (KNN, PatternList, Categories)
  301. if (my nInstances <= 0)
  302. Melder_throw (U"Instance base is empty");
  303. if (kNeighbours < 1 || kNeighbours > my nInstances)
  304. Melder_throw (U"Please select a value of k such that 0 < k < ", my nInstances + 1, U".");
  305. switch (voteWeighting) {
  306. case 1:
  307. voteWeighting = kOla_SQUARED_DISTANCE_WEIGHTED_VOTING;
  308. break;
  309. case 2:
  310. voteWeighting = kOla_DISTANCE_WEIGHTED_VOTING;
  311. break;
  312. case 3:
  313. voteWeighting = kOla_FLAT_VOTING;
  314. break;
  315. }
  316. if (your ny != his size)
  317. Melder_throw (U"The number of Categories should be equal to the number of rows in PatternList.");
  318. if (your nx != my input -> nx)
  319. Melder_throw (U"The dimensionality of PatternList should be equal to that of the instance base.");
  320. autoFeatureWeights fws = FeatureWeights_create (your nx);
  321. double result = KNN_evaluateWithTestSet (me, you, him, fws.get(), kNeighbours, voteWeighting);
  322. Melder_information (100 * result, U" percent of the instances correctly classified.");
  323. END
  324. }
  325. FORM (BUG_KNN_evaluateWithTestSetAndFeatureWeights, U"Evaluation", U"KNN & PatternList & Categories & FeatureWeights: Evaluate...") {
  326. INTEGER (kNeighbours, U"k neighbours", U"1")
  327. RADIO (voteWeighting, U"Vote weighting", 1)
  328. RADIOBUTTON (U"Inverse squared distance")
  329. RADIOBUTTON (U"Inverse distance")
  330. RADIOBUTTON (U"Flat")
  331. OK
  332. DO
  333. FIND_FOUR (KNN, PatternList, Categories, FeatureWeights)
  334. if (my nInstances <= 0)
  335. Melder_throw (U"Instance base is empty");
  336. if (kNeighbours < 1 || kNeighbours > my nInstances)
  337. Melder_throw (U"Please select a value of k such that 0 < k < ", my nInstances + 1, U".");
  338. switch (voteWeighting) {
  339. case 1:
  340. voteWeighting = kOla_SQUARED_DISTANCE_WEIGHTED_VOTING;
  341. break;
  342. case 2:
  343. voteWeighting = kOla_DISTANCE_WEIGHTED_VOTING;
  344. break;
  345. case 3:
  346. voteWeighting = kOla_FLAT_VOTING;
  347. break;
  348. }
  349. Melder_require (your ny == his size,
  350. U"Your number of Categories (", your ny, U") should be equal to the number of rows in PatternList (", his size, U").");
  351. if (your nx != my input -> nx)
  352. Melder_throw (U"The dimensionality of PatternList should be equal to that of the instance base.");
  353. if (your nx != her fweights -> numberOfColumns)
  354. Melder_throw (U"The number of feature weights should be equal to the dimensionality of the PatternList.");
  355. double result = KNN_evaluateWithTestSet (me, you, him, she, kNeighbours, voteWeighting);
  356. Melder_information (100 * result, U" percent of the instances correctly classified.");
  357. END
  358. }
  359. /////////////////////////////////////////////////////////////////////////////////////////
  360. // Classification //
  361. /////////////////////////////////////////////////////////////////////////////////////////
  362. FORM (NEW1_KNN_PatternList_to_Categories, U"Classification", U"KNN & PatternList: To Categories...") {
  363. INTEGER (kNeighbours, U"k neighbours", U"1")
  364. RADIOx (voteWeighting, U"Vote weighting", 1, 1)
  365. RADIOBUTTON (U"Inverse squared distance")
  366. RADIOBUTTON (U"Inverse distance")
  367. RADIOBUTTON (U"Flat")
  368. OK
  369. DO
  370. CONVERT_TWO (KNN, PatternList)
  371. if (my nInstances <= 0)
  372. Melder_throw (U"Instance base is empty.");
  373. if (kNeighbours < 1 || kNeighbours > my nInstances)
  374. Melder_throw (U"Please select a value of k such that 0 < k < ", my nInstances + 1, U".");
  375. switch (voteWeighting) {
  376. case 1:
  377. voteWeighting = kOla_SQUARED_DISTANCE_WEIGHTED_VOTING;
  378. break;
  379. case 2:
  380. voteWeighting = kOla_DISTANCE_WEIGHTED_VOTING;
  381. break;
  382. case 3:
  383. voteWeighting = kOla_FLAT_VOTING;
  384. break;
  385. }
  386. if (your nx != my input -> nx)
  387. Melder_throw (U"The dimensionality of PatternList should match that of the instance base.");
  388. autoFeatureWeights fws = FeatureWeights_create (your nx);
  389. autoCategories result = KNN_classifyToCategories (me, you, fws.get(), kNeighbours, voteWeighting);
  390. CONVERT_TWO_END (my name.get(), U"_", your name.get())
  391. }
  392. FORM (NEW1_KNN_PatternList_to_TableOfReal, U"Classification", U"KNN & PatternList: To TabelOfReal...") {
  393. INTEGER (kNeighbours, U"k neighbours", U"1")
  394. RADIOx (voteWeighting, U"Vote weighting", 1, 1)
  395. RADIOBUTTON (U"Inverse squared distance")
  396. RADIOBUTTON (U"Inverse distance")
  397. RADIOBUTTON (U"Flat")
  398. OK
  399. DO
  400. CONVERT_TWO (KNN, PatternList)
  401. if (my nInstances <= 0)
  402. Melder_throw (U"Instance base is empty.");
  403. if (kNeighbours < 1 || kNeighbours > my nInstances)
  404. Melder_throw (U"Please select a value of k such that 0 < k < ", my nInstances + 1, U".");
  405. autoFeatureWeights fws = FeatureWeights_create (your nx);
  406. switch (voteWeighting) {
  407. case 1:
  408. voteWeighting = kOla_SQUARED_DISTANCE_WEIGHTED_VOTING;
  409. break;
  410. case 2:
  411. voteWeighting = kOla_DISTANCE_WEIGHTED_VOTING;
  412. break;
  413. case 3:
  414. voteWeighting = kOla_FLAT_VOTING;
  415. break;
  416. }
  417. if (your nx != my input -> nx)
  418. Melder_throw (U"The dimensionality of PatternList should match that of the instance base.");
  419. autoTableOfReal result = KNN_classifyToTableOfReal (me, you, fws.get(), kNeighbours, voteWeighting);
  420. CONVERT_TWO_END (U"Output")
  421. }
  422. FORM (NEW1_KNN_PatternList_FeatureWeights_to_Categories, U"Classification", U"KNN & PatternList & FeatureWeights: To Categories...") {
  423. INTEGER (kNeighbours, U"k neighbours", U"KNN & PatternList & FeatureWeights: To Categories...")
  424. RADIOx (voteWeighting, U"Vote weighting", 1, 1)
  425. RADIOBUTTON (U"Inverse squared distance")
  426. RADIOBUTTON (U"Inverse distance")
  427. RADIOBUTTON (U"Flat")
  428. OK
  429. DO
  430. CONVERT_THREE (KNN, PatternList, FeatureWeights)
  431. if (my nInstances <= 0)
  432. Melder_throw (U"Instance base is empty.");
  433. switch (voteWeighting) {
  434. case 1:
  435. voteWeighting = kOla_SQUARED_DISTANCE_WEIGHTED_VOTING;
  436. break;
  437. case 2:
  438. voteWeighting = kOla_DISTANCE_WEIGHTED_VOTING;
  439. break;
  440. case 3:
  441. voteWeighting = kOla_FLAT_VOTING;
  442. break;
  443. }
  444. if (kNeighbours < 1 || kNeighbours > my nInstances)
  445. Melder_throw (U"Please select a value of k such that 0 < k < ", my nInstances + 1, U".");
  446. if (your nx != my input -> nx)
  447. Melder_throw (U"The dimensionality of PatternList should be equal to that of the instance base.");
  448. if (your nx != his fweights -> numberOfColumns)
  449. Melder_throw (U"The number of feature weights should be equal to the dimensionality of the PatternList.");
  450. autoCategories result = KNN_classifyToCategories (me, you, him, kNeighbours, voteWeighting);
  451. CONVERT_THREE_END (U"Output")
  452. }
  453. FORM (NEW1_KNN_PatternList_FeatureWeights_to_TableOfReal, U"Classification", U"KNN & PatternList & FeatureWeights: To TableOfReal...") {
  454. INTEGER (kNeighbours, U"k neighbours", U"1")
  455. RADIOx (voteWeighting, U"Vote weighting", 1, 1)
  456. RADIOBUTTON (U"Inverse squared distance")
  457. RADIOBUTTON (U"Inverse distance")
  458. RADIOBUTTON (U"Flat")
  459. OK
  460. DO
  461. CONVERT_THREE (KNN, PatternList, FeatureWeights)
  462. if (my nInstances <= 0)
  463. Melder_throw (U"Instance base is empty.");
  464. if (kNeighbours < 1 || kNeighbours > my nInstances)
  465. Melder_throw (U"Please select a value of k such that 0 < k < ", my nInstances + 1, U"\n");
  466. if (your nx != his fweights -> numberOfColumns)
  467. Melder_throw (U"The number of features and the number of feature weights should be equal.");
  468. switch (voteWeighting) {
  469. case 1:
  470. voteWeighting = kOla_SQUARED_DISTANCE_WEIGHTED_VOTING;
  471. break;
  472. case 2:
  473. voteWeighting = kOla_DISTANCE_WEIGHTED_VOTING;
  474. break;
  475. case 3:
  476. voteWeighting = kOla_FLAT_VOTING;
  477. break;
  478. }
  479. autoTableOfReal result = KNN_classifyToTableOfReal (me, you, him, kNeighbours, voteWeighting);
  480. CONVERT_THREE_END (U"Output")
  481. }
  482. /////////////////////////////////////////////////////////////////////////////////////////
  483. // Clustering //
  484. /////////////////////////////////////////////////////////////////////////////////////////
  485. FORM (NEW_PatternList_to_Categories_cluster, U"k-means clustering", U"PatternList: To Categories...") {
  486. INTEGER (kClusters, U"k clusters", U"1")
  487. POSITIVE (clusterSizeRatioConstraint, U"Cluster size ratio constraint", U"1e-7");
  488. INTEGER (maximumNumberOfReseeds, U"Maximum number of reseeds", U"1000")
  489. OK
  490. DO
  491. CONVERT_EACH (PatternList)
  492. if (my nx <= 0 || my ny <= 0)
  493. Melder_throw (U"PatternList is empty.");
  494. if (kClusters < 1 || kClusters > my ny)
  495. Melder_throw (U"Please select a value of k such that 0 < k <= ", my ny, U".");
  496. if (maximumNumberOfReseeds < 0)
  497. Melder_throw (U"The maximum number of reseeds should not be negative.");
  498. Melder_require (clusterSizeRatioConstraint > 0.0 && clusterSizeRatioConstraint <= 1.0,
  499. U"The cluster size ratio constraint should be between 0.0 (exclusive) and 1.0 (inclusive).");
  500. autoFeatureWeights fws = FeatureWeights_create (my nx);
  501. autoCategories result = PatternList_to_Categories_cluster (me, fws.get(), kClusters, clusterSizeRatioConstraint, maximumNumberOfReseeds);
  502. CONVERT_EACH_END (U"Output")
  503. }
  504. FORM (NEW1_PatternList_FeatureWeights_to_Categories_cluster, U"k-means clustering", U"PatternList & FeatureWeights: To Categories...") {
  505. INTEGER (kClusters, U"k clusters", U"1")
  506. POSITIVE (clusterSizeRatioConstraint, U"Cluster size ratio constraint", U"1e-7");
  507. INTEGER (maximumNumberOfReseeds, U"Maximum number of reseeds", U"1000")
  508. OK
  509. DO
  510. CONVERT_TWO (PatternList, FeatureWeights)
  511. if (my nx <= 0 || my ny <= 0)
  512. Melder_throw (U"PatternList is empty.");
  513. if (my nx != your fweights -> numberOfColumns)
  514. Melder_throw (U"The number of features and the number of feature weights should be equal.");
  515. if (kClusters < 1 || kClusters > my ny)
  516. Melder_throw (U"Please select a value of k such that 0 < k <= ", my ny, U".");
  517. Melder_require (maximumNumberOfReseeds >= 0,
  518. U"The maximum number of reseeds should be 0 or positive.");
  519. Melder_require (clusterSizeRatioConstraint > 0.0 && clusterSizeRatioConstraint <= 1.0,
  520. U"The cluster size ratio constraint should be between 0.0 (exclusive) and 1.0 (inclusive).");
  521. autoCategories result = PatternList_to_Categories_cluster (me, you, kClusters, clusterSizeRatioConstraint, maximumNumberOfReseeds);
  522. CONVERT_TWO_END (U"Output")
  523. }
  524. /////////////////////////////////////////////////////////////////////////////////////////
  525. // Dissimilarity computations //
  526. /////////////////////////////////////////////////////////////////////////////////////////
  527. DIRECT (NEW_PatternList_to_Dissimilarity) {
  528. CONVERT_EACH (PatternList)
  529. autoFeatureWeights fws = FeatureWeights_create (my nx);
  530. autoDissimilarity result = KNN_patternToDissimilarity (me, fws.get());
  531. CONVERT_EACH_END (my name.get())
  532. }
  533. DIRECT (NEW1_PatternList_FeatureWeights_to_Dissimilarity) {
  534. CONVERT_TWO (PatternList, FeatureWeights)
  535. if (my nx != your fweights -> numberOfColumns)
  536. Melder_throw (U"The number of features and the number of feature weights should be equal.");
  537. autoDissimilarity result = KNN_patternToDissimilarity (me, you);
  538. CONVERT_TWO_END (U"Output")
  539. }
  540. /////////////////////////////////////////////////////////////////////////////////////////
  541. // Computation of permutation //
  542. /////////////////////////////////////////////////////////////////////////////////////////
  543. FORM (NEW_KNN_to_Permutation_annealing, U"KNN: To Permutation", U"PatternList & Categories: To FeatureWeights...") {
  544. NATURAL (numberOfTriesPerStep, U"Number of tries per step", U"200")
  545. NATURAL (numberOfIterations, U"Number of iterations", U"10")
  546. POSITIVE (stepSize, U"Step size", U"10")
  547. POSITIVE (boltzmannConstant, U"Boltzmann constant", U"1.0")
  548. POSITIVE (initialTemperature, U"Initial temperature", U"0.002")
  549. POSITIVE (dampingFactor, U"Damping factor", U"1.005")
  550. POSITIVE (finalTemperature, U"Final temperature", U"0.000002")
  551. OK
  552. DO
  553. CONVERT_EACH (KNN)
  554. autoPermutation result = KNN_SA_ToPermutation (me, numberOfTriesPerStep, numberOfIterations,
  555. stepSize, boltzmannConstant, initialTemperature, dampingFactor, finalTemperature);
  556. CONVERT_EACH_END (my name.get())
  557. }
  558. /////////////////////////////////////////////////////////////////////////////////////////
  559. // Computation of feature weights //
  560. /////////////////////////////////////////////////////////////////////////////////////////
  561. FORM (NEW1_PatternList_Categories_to_FeatureWeights_relief, U"Feature weights", U"PatternList & Categories: To FeatureWeights...") {
  562. INTEGER (numberOfNeighbours, U"Number of neighbours", U"1")
  563. OK
  564. DO
  565. CONVERT_TWO (PatternList, Categories)
  566. if (my ny < 2)
  567. Melder_throw (U"The PatternList object should contain at least two rows.");
  568. if (my ny != your size)
  569. Melder_throw (U"The number of rows in the PatternList object should equal the number of categories in the Categories object.");
  570. autoFeatureWeights result = FeatureWeights_compute (me, you, numberOfNeighbours);
  571. CONVERT_TWO_END (U"Output")
  572. }
  573. FORM (NEW1_KNN_PatternList_Categories_to_FeatureWeights_wrapperExt, U"Feature weights", U"KNN & PatternList & Categories: To FeatureWeights..") {
  574. POSITIVE (learningRate, U"Learning rate", U"0.02")
  575. NATURAL (numberOfSeeds, U"Number of seeds", U"20")
  576. POSITIVE (stopAt, U"Stop at", U"1.0")
  577. RADIOx (optimization, U"Optimization", 1, 1)
  578. RADIOBUTTON (U"Co-optimization")
  579. RADIOBUTTON (U"Single feature")
  580. NATURAL (kNeighbours, U"k neighbours", U"1")
  581. RADIOx (voteWeighting, U"Vote weighting", 3, 1)
  582. RADIOBUTTON (U"Inverse squared distance")
  583. RADIOBUTTON (U"Inverse distance")
  584. RADIOBUTTON (U"Flat")
  585. OK
  586. DO
  587. CONVERT_THREE (KNN, PatternList, Categories)
  588. if (my nInstances <= 0)
  589. Melder_throw (U"Instance base is empty");
  590. switch (voteWeighting) {
  591. case 1:
  592. voteWeighting = kOla_SQUARED_DISTANCE_WEIGHTED_VOTING;
  593. break;
  594. case 2:
  595. voteWeighting = kOla_DISTANCE_WEIGHTED_VOTING;
  596. break;
  597. case 3:
  598. voteWeighting = kOla_FLAT_VOTING;
  599. break;
  600. }
  601. if (kNeighbours < 1 || kNeighbours > my nInstances)
  602. Melder_throw (U"Please select a value of k such that 0 < k < ", my nInstances + 1, U".");
  603. if (your nx != my input -> nx)
  604. Melder_throw (U"The dimensionality of PatternList should be equal to that of the instance base.");
  605. autoFeatureWeights result = FeatureWeights_computeWrapperExt (me, you, him, kNeighbours, voteWeighting, numberOfSeeds,
  606. learningRate, stopAt, optimization);
  607. CONVERT_THREE_END (U"Output")
  608. }
  609. FORM (NEW_KNN_to_FeatureWeights_wrapperInt, U"Feature weights", U"KNN: To FeatureWeights...") {
  610. POSITIVE (learningRate, U"Learning rate", U"0.02")
  611. NATURAL (numberOfSeeds, U"Number of seeds", U"10")
  612. POSITIVE (stopAt, U"Stop at", U"1.0")
  613. RADIOx (optimization, U"Optimization", 1, 1)
  614. RADIOBUTTON (U"Co-optimization")
  615. RADIOBUTTON (U"Single feature")
  616. RADIOx (evaluationMethod, U"Evaluation method", 1, 1)
  617. RADIOBUTTON (U"Leave one out")
  618. RADIOBUTTON (U"10-fold cross-validation")
  619. NATURAL (kNeighbours, U"k neighbours", U"1")
  620. RADIOx (voteWeighting, U"Vote weighting", 3, 1)
  621. RADIOBUTTON (U"Inverse squared distance")
  622. RADIOBUTTON (U"Inverse distance")
  623. RADIOBUTTON (U"Flat")
  624. OK
  625. DO
  626. switch (evaluationMethod) {
  627. case 1:
  628. evaluationMethod = kOla_LEAVE_ONE_OUT;
  629. break;
  630. case 2:
  631. evaluationMethod = kOla_TEN_FOLD_CROSS_VALIDATION;
  632. break;
  633. }
  634. switch (voteWeighting) {
  635. case 1:
  636. voteWeighting = kOla_SQUARED_DISTANCE_WEIGHTED_VOTING;
  637. break;
  638. case 2:
  639. voteWeighting = kOla_DISTANCE_WEIGHTED_VOTING;
  640. break;
  641. case 3:
  642. voteWeighting = kOla_FLAT_VOTING;
  643. break;
  644. }
  645. CONVERT_EACH (KNN)
  646. if (my nInstances < 1)
  647. Melder_throw (U"Instance base is empty");
  648. if (kNeighbours < 1 || kNeighbours > my nInstances)
  649. Melder_throw (U"Please select a value of k such that 0 < k < ", my nInstances + 1, U".");
  650. autoFeatureWeights result = FeatureWeights_computeWrapperInt (me, kNeighbours, voteWeighting, numberOfSeeds, learningRate,
  651. stopAt, optimization, evaluationMethod);
  652. CONVERT_EACH_END (my name.get(), U"_output")
  653. }
  654. /////////////////////////////////////////////////////////////////////////////////////////
  655. // Creation and processing of auxiliary datatypes //
  656. /////////////////////////////////////////////////////////////////////////////////////////
  657. FORM (NEW1_FeatureWeights_create, U"Create FeatureWeights", nullptr) {
  658. WORD (name, U"Name", U"empty")
  659. NATURAL (numberOfWeights, U"Number of weights", U"1")
  660. OK
  661. DO
  662. CREATE_ONE
  663. autoFeatureWeights result = FeatureWeights_create (numberOfWeights);
  664. CREATE_ONE_END (name)
  665. }
  666. /////////////////////////////////////////////////////////////////////////////////////////
  667. // DEBUG //
  668. /////////////////////////////////////////////////////////////////////////////////////////
  669. // Disabled
  670. /*
  671. #ifdef _DEBUG
  672. DIRECT (KNN_debug_KNN_SA_partition) {
  673. FIND_ONE (PatternList)
  674. autoPatternList output = PatternList_create (my ny, my nx);
  675. autoNUMvector <integer> result (0, my ny);
  676. KNN_SA_partition (me, 1, my ny, result);
  677. for (integer k = 1, c = 1; k <= output -> ny; k ++, c ++)
  678. for (integer i = 1; i <= my ny && k <= output -> ny; i ++)
  679. if (result [i] == c) {
  680. for(integer j = 1; j <= output -> nx; ++j)
  681. output -> z [k] [j] = my z [i] [j];
  682. k ++;
  683. }
  684. praat_new (output.move(), U"Output");
  685. END
  686. }
  687. DIRECT (KNN_debug_KNN_getNumberOfCPUs) {
  688. Melder_information (KNN_getNumberOfCPUs(), U" CPUs available");
  689. END }
  690. DIRECT (KNN_debug_KNN_threadTest) {
  691. KNN_threadTest();
  692. END }
  693. #endif
  694. */
  695. /////////////////////////////////////////////////////////////////////////////////////////
  696. // Help //
  697. /////////////////////////////////////////////////////////////////////////////////////////
  698. DIRECT (HELP_KNN_help) {
  699. HELP (U"KNN classifiers")
  700. }
  701. DIRECT (HINT_KNN_and_FeatureWeights_evaluate) {
  702. INFO_NONE
  703. Melder_information (U"The accuracy of a KNN can be estimated by selecting a KNN and a FeatureWeights object and choosing \"Evaluate...\".");
  704. INFO_NONE_END
  705. }
  706. DIRECT (HINT_KNN_and_Pattern_classify) {
  707. INFO_NONE
  708. Melder_information (U"You can use the KNN as a classifier by selecting a KNN and a PatternList and choosing \"To Categories...\" or \"To TableOfReal...\".");
  709. INFO_NONE_END
  710. }
  711. DIRECT (HINT_KNN_and_Pattern_and_FeatureWeights_classify) {
  712. INFO_NONE
  713. Melder_information (U"You can use the KNN as a classifier by selecting a KNN, a PatternList and an FeatureWeights object and choosing \"To Categories...\" or \"To TableOfReal...\".");
  714. INFO_NONE_END
  715. }
  716. DIRECT (HINT_KNN_and_Pattern_and_Categories_learn) {
  717. INFO_NONE
  718. Melder_information (U"You can train a KNN by selecting a KNN, a PatternList and a Categories object together and choosing \"Learn...\".");
  719. INFO_NONE_END
  720. }
  721. DIRECT (HINT_KNN_and_Pattern_and_Categories_evaluate) {
  722. INFO_NONE
  723. Melder_information (U"The accuracy of a KNN can be estimated by selecting a KNN, a test PatternList and the corresponding Categories object and choosing \"Evaluate...\".");
  724. INFO_NONE_END
  725. }
  726. DIRECT (HINT_KNN_and_Pattern_and_Categories_and_FeatureWeights_evaluate) {
  727. INFO_NONE
  728. Melder_information (U"The accuracy of a KNN can be estimated by selecting a KNN, a test PatternList, an FeatureWeights object, and the corresponding Categories object and choosing \"Evaluate...\".");
  729. INFO_NONE_END
  730. }
  731. DIRECT (HINT_Pattern_and_FeatureWeights_to_Categories) {
  732. INFO_NONE
  733. Melder_information (U"A PatternList object and a FeatureWeights object can be used to compute a fixed number of clusters using the k-means clustering clustering algorithm.");
  734. INFO_NONE_END
  735. }
  736. DIRECT (HINT_Pattern_and_FeatureWeights_to_Dissimilarity) {
  737. INFO_NONE
  738. Melder_information (U"A Dissimilarity matrix can be generated from a PatternList and a FeatureWeights object.");
  739. INFO_NONE_END
  740. }
  741. /////////////////////////////////////////////////////////////////////////////////////////
  742. // Setting callbacks //
  743. /////////////////////////////////////////////////////////////////////////////////////////
  744. void praat_contrib_Ola_KNN_init ();
  745. void praat_contrib_Ola_KNN_init ()
  746. {
  747. Thing_recognizeClassesByName (classKNN, classFeatureWeights, nullptr);
  748. //////////
  749. // Menu //
  750. //////////
  751. praat_addMenuCommand (U"Objects", U"New", U"kNN classifiers", nullptr, 0, nullptr);
  752. praat_addMenuCommand (U"Objects", U"New", U"kNN classifiers", nullptr, praat_DEPTH_1, HELP_KNN_help);
  753. praat_addMenuCommand (U"Objects", U"New", U"-- KNN --", nullptr, 1, nullptr);
  754. praat_addMenuCommand (U"Objects", U"New", U"Create kNN classifier...", nullptr, 1, NEW1_KNN_create);
  755. praat_addMenuCommand (U"Objects", U"New", U"Advanced", nullptr, 1, nullptr);
  756. praat_addMenuCommand (U"Objects", U"New", U"Create PatternList...", nullptr, 2, NEW1_PatternList_create);
  757. praat_addMenuCommand (U"Objects", U"New", U"Create Categories...", nullptr, 2, NEW1_Categories_create);
  758. praat_addMenuCommand (U"Objects", U"New", U"Create FeatureWeights...", nullptr, 2, NEW1_FeatureWeights_create);
  759. /////////////
  760. // Actions //
  761. /////////////
  762. praat_addAction1 (classKNN, 0, U"kNN help", nullptr, 0, HELP_KNN_help);
  763. praat_addAction1 (classKNN, 0, QUERY_BUTTON, nullptr, 0, nullptr);
  764. praat_addAction1 (classKNN, 1, U"Get optimized parameters...", nullptr, 2, INTEGER_KNN_getOptimumModel);
  765. praat_addAction1 (classKNN, 1, U"Get accuracy estimate...", nullptr, 2, REAL_KNN_evaluate);
  766. praat_addAction1 (classKNN, 1, U"Get size of instancebase", nullptr, 2, INTEGER_KNN_getNumberOfInstances);
  767. praat_addAction1 (classKNN, 0, MODIFY_BUTTON, nullptr, 0, nullptr);
  768. praat_addAction1 (classKNN, 0, U"Shuffle", nullptr, 1, MODIFY_KNN_shuffle);
  769. praat_addAction1 (classKNN, 1, U"Prune...", nullptr, 1, INFO_MODIFY_KNN_prune);
  770. praat_addAction1 (classKNN, 0, U"Reset...", nullptr, 1, MODIFY_KNN_reset);
  771. praat_addAction1 (classKNN, 0, EXTRACT_BUTTON, nullptr, 0, nullptr);
  772. praat_addAction1 (classKNN, 0, U"Extract input Patterns", nullptr, 1, NEW_KNN_extractInputPatterns);
  773. praat_addAction1 (classKNN, 0, U"Extract output Categories", nullptr, 1, NEW_KNN_extractOutputCategories);
  774. praat_addAction1 (classKNN, 0, U"To FeatureWeights...", nullptr, 0, NEW_KNN_to_FeatureWeights_wrapperInt);
  775. // praat_addAction1 (classKNN, 0, U"To Permutation (annealing)...", nullptr, 0, NEW_KNN_to_Permutation_annealing);
  776. // praat_addAction2 (classKNN, 1, classFeatureWeights, 1, U"To Permutation...", nullptr, 0, NEW1_KNN_FeatureWeights_to_Permutation);
  777. praat_addAction3 (classKNN, 1, classPatternList, 1, classCategories, 1, U"Learn...", nullptr, 0, MODIFY_KNN_PatternList_Categories_learn);
  778. praat_addAction2 (classKNN, 1, classFeatureWeights, 1, U"Evaluate...", nullptr, 0, REAL_KNN_FeatureWeights_evaluate);
  779. praat_addAction3 (classKNN, 1, classPatternList, 1, classCategories, 1, U"Evaluate...", nullptr, 0, BUG_KNN_evaluateWithTestSet);
  780. praat_addAction4 (classKNN, 1, classPatternList, 1, classCategories, 1, classFeatureWeights, 1, U"Evaluate...", nullptr, 0, BUG_KNN_evaluateWithTestSetAndFeatureWeights);
  781. praat_addAction3 (classKNN, 1, classPatternList, 1, classCategories, 1, U"To FeatureWeights...", nullptr, 0,
  782. NEW1_KNN_PatternList_Categories_to_FeatureWeights_wrapperExt);
  783. praat_addAction2 (classKNN, 1, classPatternList, 1, U"To Categories...", nullptr, 0, NEW1_KNN_PatternList_to_Categories);
  784. praat_addAction2 (classKNN, 1, classPatternList, 1, U"To TableOfReal...", nullptr, 0, NEW1_KNN_PatternList_to_TableOfReal);
  785. praat_addAction3 (classKNN, 1, classPatternList, 1, classFeatureWeights, 1, U"To Categories...", nullptr, 0,
  786. NEW1_KNN_PatternList_FeatureWeights_to_Categories);
  787. praat_addAction3 (classKNN, 1, classPatternList, 1, classFeatureWeights, 1, U"To TableOfReal...", nullptr, 0,
  788. NEW1_KNN_PatternList_FeatureWeights_to_TableOfReal);
  789. praat_addAction1 (classPatternList, 0, U"To Dissimilarity", nullptr, 1, NEW_PatternList_to_Dissimilarity);
  790. praat_addAction1 (classPatternList, 0, U"To Categories (cluster)...", nullptr, 1, NEW_PatternList_to_Categories_cluster);
  791. praat_addAction2 (classPatternList, 1, classFeatureWeights, 1, U"To Dissimilarity", nullptr, 0, NEW1_PatternList_FeatureWeights_to_Dissimilarity);
  792. praat_addAction2 (classPatternList, 1, classFeatureWeights, 1, U"To Categories (cluster)...", nullptr, 0, NEW1_PatternList_FeatureWeights_to_Categories_cluster);
  793. praat_addAction2 (classPatternList, 1, classCategories, 1, U"To FeatureWeights (relief)...", nullptr, 0, NEW1_PatternList_Categories_to_FeatureWeights_relief);
  794. praat_addAction2 (classPatternList, 1, classCategories, 1, U"To KNN Classifier...", nullptr, 0, NEW1_PatternList_Categories_to_KNN);
  795. ///////////
  796. // DEBUG //
  797. ///////////
  798. /*
  799. #ifdef _DEBUG
  800. praat_addAction1 (classKNN, 0, U"_DEBUG: KNN_getNumberOfCPUs", 0, 0, DO_KNN_debug_KNN_getNumberOfCPUs);
  801. praat_addAction1 (classKNN, 0, U"_DEBUG: KNN_threadTest", 0, 0, DO_KNN_debug_KNN_threadTest);
  802. praat_addAction1 (classPattern, 1, U"_DEBUG: KNN_SA_partition", 0, 1, DO_KNN_debug_KNN_SA_partition);
  803. #endif
  804. */
  805. ///////////
  806. // Hints //
  807. ///////////
  808. praat_addAction1 (classPatternList, 0, U"& FeatureWeights: To Categories?", nullptr, 0, HINT_Pattern_and_FeatureWeights_to_Categories);
  809. praat_addAction1 (classPatternList, 0, U"& FeatureWeights: To Dissimilarity?", nullptr, 0, HINT_Pattern_and_FeatureWeights_to_Dissimilarity);
  810. praat_addAction1 (classKNN, 0, U"& FeatureWeights: Evaluate?", nullptr, 0, HINT_KNN_and_FeatureWeights_evaluate);
  811. // praat_addAction1 (classKNN, 0, U"& FeatureWeights: To Permutation?", nullptr, 0, HINT_Pattern_and_FeatureWeights_to_Dissimilarity);
  812. praat_addAction1 (classKNN, 0, U"& Pattern: Classify?", nullptr, 0, HINT_KNN_and_Pattern_classify);
  813. praat_addAction1 (classKNN, 0, U"& Pattern & FeatureWeights: Classify?", nullptr, 0, HINT_KNN_and_Pattern_and_FeatureWeights_classify);
  814. praat_addAction1 (classKNN, 0, U"& Pattern & Categories: Learn?", nullptr, 0, HINT_KNN_and_Pattern_and_Categories_learn);
  815. praat_addAction1 (classKNN, 0, U"& Pattern & Categories: Evaluate?", nullptr, 0, HINT_KNN_and_Pattern_and_Categories_evaluate);
  816. praat_addAction1 (classKNN, 0, U"& Pattern & Categories & FeatureWeights: Evaluate?", nullptr, 0, HINT_KNN_and_Pattern_and_Categories_and_FeatureWeights_evaluate);
  817. INCLUDE_MANPAGES (manual_KNN_init)
  818. }
  819. /* End of file praat_contrib_Ola_KNN.cpp */