Planet CLAM
http://clam-project.org
Planet CLAM - http://clam-project.orgXavier Amatriain: Where am I?
http://technocalifornia.blogspot.com/2016/02/where-am-i.html
There have recently been some articles (e.g. <a href="https://www.poweradmin.com/blog/51-devops-influencers-to-start-following-today/">This</a> list of influencers) that have pointed to this blog and lamented that I don't update it regularly anymore. It is true. I now realize I should have at least posted something here to direct readers to the places where I keep posting in case they find I might have something interesting to say.<br /><br />First and foremost, given that I joined <a href="http://www.quora.com/">Quora</a> about a year ago, I have been using the Quora product itself to post most of my writing. You can find my profile <a href="https://www.quora.com/profile/Xavier-Amatriain">here</a>. I have found that I can reformulate almost anything I want to say in the form of an answer to a Quora question. Besides, my posts there get a ton of views (I am almost about to reach 2 million views in about a year) and good interactions. Also, I have written some posts in the <a href="https://engineering.quora.com/">Quora Engineering Blog</a> describing some of our work.<br /><br />I also keep very active on <a href="https://twitter.com/xamat">Twitter</a>, and every now and then I will update my <a href="https://www.linkedin.com/in/xamatriain">LinkedIn</a> with some professional posts.<br /><br />Recently, I gave <a href="https://medium.com/@xamat">Medium</a> a try. I am not really sure how often I will update my blog there, but I am almost certain that my Medium blog will take precedence over this one. Medium is indeed a much better blogging platform than Blogger.<br /><br />So, yes, I guess this is a farewell to Technocalifornia unless every now and then I decide to simply post a collection of posts elsewhere just to make sure that people visiting this blog don't get the sense that I am not active anymore. Let me know if you feel that would be interesting for you.2016-02-02T18:35:10+00:00Xavier AmatriainXavier Amatriain: Ten Lessons Learned from Building (real-life impactful) Machine Learning Systems
http://technocalifornia.blogspot.com/2014/12/ten-lessons-learned-from-building-real.html
(This is a blogpost version of a talk I gave at <a href="http://mlconf.com/mlconf-sf/">MLConf SF</a> 11/14/2014. See below for original video and slides)<br /><br />There are many good textbooks and courses where you can be introduced to machine learning and maybe even learn some of the most intricate details about a particular approach or algorithm (See <a href="http://www.quora.com/What-are-the-best-talks-lectures-related-to-big-data-algorithms-machine-learning/answer/Xavier-Amatriain">my answer on Quora </a>on what are good resources for this). While understanding that theory is a very important base and starting point, there are many other practical issues related to building real-life ML systems that you don’t usually hear about. In this post I will share some of the most important lessons learned in years of building large-scale ML solutions that power products such as Netflix and scale to millions of users across many countries.<br /><br />And just in case it doesn't come across clearly enough, let me insist on this once again: it does pay off to be knowledgeable and have deep understanding of the techniques and theory behind classic and modern machine learning approaches. Understanding how Logistic Regression works or the difference between Factorization Machines and Tensor Factorization, for example, is a necessary starting point. However, this in itself might not be enough unless you couple it with the real-life experience of how these models interact with systems, data, and users in order to obtain a really valuable impact. The next ten lessons are my attempt at trying to capture some of that practical knowledge.<br /><br /><h2>1. More Data <strike>vs.</strike> and Better Models </h2>A lot has been written about whether the key to better results lays in improving your algorithms or simply on throwing more data at your problem (see <a href="http://technocalifornia.blogspot.com/2012/07/more-data-or-better-models.html">my post</a> from 2012 discussing this same topic, for example).<br /><br />In the context of the <a href="http://www.netflixprize.com/">Netflix Prize</a>, <a href="http://anand.typepad.com/datawocky/anand-rajaraman.html">Anand Rajaraman</a> took an early stand on the issue by claiming that "more data usually beats better algorithms". In <a href="http://anand.typepad.com/datawocky/2008/03/more-data-usual.html">his post</a> he explained how some of his students had improved some of the existing results on the Netflix ratings dataset by adding metadata from <a href="http://www.imdb.com/">IMDB</a>.<br /><br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><img height="260" id="docs-internal-guid-a151bd0c-0d4f-1450-e347-fce69844d204" src="https://lh5.googleusercontent.com/cLDPJ8z2wGz_cvgtbJBACyNh4T-f77_yh_Y82RWHdvQJqNKiSKzx3qxgkBIFsPYGtH7TA5pqdq8KKkY8qU36TvkzdUWZNTWHWKs70g98hrj_dhwqlsrwcxIfzTEX8iz1A34E" style="margin-left: auto; margin-right: auto;" width="400" /></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 1.</b> More data usually beats better algorithms</td></tr></tbody></table><br />Although many teams in the competition tried to follow that lead and add extra features to improve results, there was little progress in that direction. As a matter of fact, just a year later some of the leaders of what would become the runner up team published <a href="http://dl.acm.org/citation.cfm?id=1639731">a paper</a> in which they showed that adding metadata had very little impact in improving the prediction accuracy of a well-tuned algorithm. Take this as a first example of why adding more data is not always the solution.<br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><img height="258" id="docs-internal-guid-a151bd2c-0d4f-7605-22fb-f3c291a9cf2c" src="https://lh6.googleusercontent.com/UdGUvRU2Hmux2zAiOzkmI9h4ZyXz4r5XW0A4_uthtk6XgcNpXttV6XVZGzcIqnXQqUIytY9BraFQqhL-px8aVpjT6HlihJVxaI2RWcJHoUhUSd7EBWw_ZE_ueCn6ojdKPDkb" style="margin-left: auto; margin-right: auto;" width="400" /></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 2.</b> Even a Few Ratings Are More Valuable than Metadata</td></tr></tbody></table><br />Of course, there are different ways to "add more data". In the example above we were adding data by increasing the number and types of features, therefore increasing the dimensionality of our problem space. We can think about adding data in a completely different way by fixing the space dimensionality and simply throwing more training examples at it. Banko and Brill <a href="http://dl.acm.org/citation.cfm?id=1073017">showed </a>in 2001 that in some cases very different algorithms responded equally well by improving to more training data (see figure below)<br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><img height="315px;" id="docs-internal-guid-a151bd33-0d4f-d7b1-f28a-c49ef172952d" src="https://lh3.googleusercontent.com/OfMe_wqQCONpoNbBrOzJNARrhj5TDF5cqWNMy5w65O6Nb5SKodAcP3DdIGtAH_CqoBnTn023laHgQ6VZmWpak5bPvmzczef06vJ4HWPBMWZ1eAw-APLrJFqLP-2YK3D5DEjN" style="margin-left: auto; margin-right: auto;" width="289px;" /></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 3.</b> Banko and Brill's "famous" model performance curves </td></tr></tbody></table><div>Google's Research Director and renowned AI figure Peter Norvig is quoted as saying that "Google does not have better algorithms, just more data". In fact, Norvig is one of the co-authors of "<a href="http://static.googleusercontent.com/media/research.google.com/en/us/pubs/archive/35179.pdf">The Unreasonable Effectiveness of Data</a>" where in a similar problem to the one in Banko and Brill (language understanding) they also show how important it is to have "more data".</div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><img height="148px;" id="docs-internal-guid-a151bd33-0d4f-ff37-acb4-193a48b711f1" src="https://lh4.googleusercontent.com/3K9GF2qX97K0hAeu61Es0O3j6t-oTZgvfeMEQUu94g3UQUTQXH3Ed_SS_oqMHTkGH-Z0vLW8RfFboB9gYDGEm2ghjecI4qm_r-pXLVfOw7-pNP8s8K-gP84Jc6bwfWWZgD_l" style="margin-left: auto; margin-right: auto;" width="371px;" /></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 4.</b> The Unreasonable Effectiveness of Data </td></tr></tbody></table><div>So, is it true that more data in the form of more training examples will always help? Well, not really. The problems above are complex models with a huge number of features which lead to situations of "<a href="http://en.wikipedia.org/wiki/Bias%E2%80%93variance_tradeoff">high variance</a>". But, in many other cases this might not be true. See below for example a real-case scenario of an algorithm in production at Netflix. In this case, adding more than 2 million training examples has very little to no effect.</div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><img height="202" id="docs-internal-guid-a151bd6a-0d50-23e0-fede-27877995569a" src="https://lh6.googleusercontent.com/iIRqH3yADGbKXS94ZIoy7P_ArXjc6BldQb3uFsfBGPxx84O4t9KYTl_6QGlfwJnyurSbCEn_E2J1ozDmJfggDOfmr-CroI_i_xF9TnSS1AZVFT1Nw74tt6n8ImCvBdYpQIKh" style="margin-left: auto; margin-right: auto;" width="400" /></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 5.</b> Testing Accuracy of a real-life production model</td></tr></tbody></table><h2 style="text-align: center;"> </h2><div>So, this leads to our first lesson learned, which in fact will expand over several of the following ones: it is not about more data versus better algorithms. That is a false dichotomy. Sometimes you need more data, and sometimes you don't. Sometimes you might need to improve your algorithm and in others it will make no difference. Focusing exclusively on one or the other will lead to far from optimal results.</div><h2>2. You might not need all your "Big Data"</h2><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">This second lesson is in fact a corollary of the previous one, but I feel it is worth to mention explicitly on its own. It seems like nowadays everyone needs to make use of all their "Big Data". Big Data is so hyped that it seems like if you are not using huge quantities of data you must be doing something wrong. The </span></span><span style="line-height: 16px; white-space: pre-wrap;">truth</span><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"> though, as discussed in lesson 1, is that there are many problems for which you might be able to get similar results by using much less data than the one you have available.</span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">Think for example of the Netflix Prize where you had 0.5 Million users in the dataset. In the most favored approach, the data was used to compute a Matrix of 50 factors. Would the result change much if instead of the 0.5 M users you used, say 50 Million? Probably not.</span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">A related, and important, question is how do you determine what subset of your data to use. A good initial approach would be to random sample your original data to obtain as many samples you need for your model training. That might not be good enough though. Staying with the Netflix Prize example, users might be very different and not homogeneously distributed in our original population. New users, for example, will have much fewer ratings and increase sparsity in the dataset. On the other hand, they might have a different behavior from more tenured users and we might want to make our model capture it. The solution is to use some form of <a href="http://en.wikipedia.org/wiki/Stratified_sampling">stratified sampling</a>. Setting up a good stratified sampling scheme is not easy since it requires us to define the different strata, and decide what is the right combination of samples for the model to learn. However, as surprising as it might sound, a well-defined stratified sampled subset might accomplish even better results than the original complete dataset.</span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="line-height: 16px; white-space: pre-wrap;">Just to be clear, I am not saying that having lots of data is a bad thing, of course it is not. The more data you have, the more choices you will be able to make on how to use it. All I am saying is that focusing on the "size" of your data versus the quality of the information in the data is a mistake. Garner the ability to use as much data as you can in your systems and then use only as much as you need to solve your problems.</span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><br /><h2>3. The fact that a more complex Model does not improve things does not mean you don't need one</h2><div><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">Imagine the following scenario: </span><span style="line-height: 1; white-space: pre-wrap;">You have a linear model and for some time you have been selecting and optimizing features for that model. One day you decide to </span><span style="vertical-align: baseline; white-space: pre-wrap;">try a </span><span style="vertical-align: baseline; white-space: pre-wrap;">more complex</span><span style="vertical-align: baseline; white-space: pre-wrap;"> (e.g. non-linear) </span><span style="vertical-align: baseline; white-space: pre-wrap;">model</span><span style="vertical-align: baseline; white-space: pre-wrap;"> with the same features you have been engineering. Most likely, you will </span><span style="vertical-align: baseline; white-space: pre-wrap;">not</span><span style="vertical-align: baseline; white-space: pre-wrap;"> see any </span><span style="vertical-align: baseline; white-space: pre-wrap;">improvement.</span><br /><span style="vertical-align: baseline; white-space: pre-wrap;"><br /></span></span><br /><span style="font-family: inherit;"><span style="vertical-align: baseline; white-space: pre-wrap;">After that failure, you change your strategy and try to do the opposite: You keep the old model, but </span></span><br /><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;">add </span><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;">more</span><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;"> expressive </span><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;">features that try to capture more complex interactions. Most likely the result will be the same and you will again see little to no improvements.</span><br /><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;">So, what is going on? The issue here is that simply put more complex features require a more complex model, and vice versa, a more complex model may require more complex features before showing any significant improvement.</span><br /><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;"><br /></span>So, the lesson learned is that you must improve both your model and your feature set in parallel. Doing only one of them at a time might lead to wrong conclusions.<br /><br /></div><h2>4. Be thoughtful about how you define your training/testing data sets</h2><div><span style="line-height: 16px; white-space: pre-wrap;">If you are training a simple binary classifier, one of the first tasks to do is to define your positive and negative examples. Defining positive and negative labels for samples though may not be such a trivial task. Think about a use case where you need to define a classifier to distinguish between shows that users watch (positives) and do not watch (negatives). In that context, would the following be positives or negatives?</span><br /><ul><li><span style="font-family: inherit; line-height: 1; white-space: pre-wrap;">User watches a movie to completion and rates it 1 star</span></li><li><span style="font-family: inherit; line-height: 1; white-space: pre-wrap;">User watches the same movie again (maybe because she can’t find anything else)</span></li><li><span style="font-family: inherit; line-height: 1; white-space: pre-wrap;">User abandons movie after 5 minutes, or 15 minutes… or 1 hour</span></li><li><span style="font-family: inherit; line-height: 1; white-space: pre-wrap;">User abandons TV show after 2 episodes, or 10 episode… or 1 season</span></li><li><span style="font-family: inherit; white-space: pre-wrap;">User adds something to her list but never watches it</span></li></ul><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">As you can see, determining whether a given example is a positive or a negative is not so easy.</span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">Besides paying attention to your positive and negative definition, there are many other things you need to make sure to get right when defining your training and testing datasets. One such issue is what we call <i>Time Travelling</i>. </span></span>Time traveling is defined as usage of features that originated after the event you are trying to predict. <span style="vertical-align: baseline;">E.g. </span><span style="font-style: italic; vertical-align: baseline;">Your rating a movie is a pretty good prediction of you watching that movie, especially because most ratings happen AFTER you watch the movie.</span><span style="font-style: italic; vertical-align: baseline;"><br /></span>In simple cases as the example above this effect might seem obvious. However, things can get very tricky when you have many features that come from different sources and pipelines and relate to each other in non-obvious ways. </span></span><span style="font-family: inherit;"><span style="white-space: pre-wrap;"><i>Time traveling</i> has the effect of increasing model performance beyond what would seem reasonable. That is why whenever you see an offline experiment with huge wins, the first question you might want to ask yourself is: “Am I time traveling?”.</span></span><span id="docs-internal-guid-b10ab187-3d2a-e838-b4e4-bffc8cd4eb91"></span><br /><span style="font-family: inherit;"><span style="white-space: pre-wrap;"><br /></span></span><span style="font-family: inherit;"><span style="white-space: pre-wrap;">And, remember, Time Traveling and positive/negative selection are just two examples of issues you might encounter when defining your training and testing datasets. Just make sure you are thoughtful about how you define all the details of your datasets.</span></span><br /><span style="font-family: inherit;"><span style="white-space: pre-wrap;"><br /></span></span><br /><div><span id="docs-internal-guid-b10ab187-3d2a-e838-b4e4-bffc8cd4eb91"><span id="docs-internal-guid-b10ab18c-3d2b-58de-0f93-9fb017560787"></span></span></div><span id="docs-internal-guid-b10ab187-3d2a-e838-b4e4-bffc8cd4eb91"></span></div><h2>5. Learn to deal with (the curse of) the Presentation Bias</h2><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-LE-cs0evhcQ/VIqKvGIBVtI/AAAAAAAAA6M/Y7_dKcICg08/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A26%3A27.png" style="margin-left: auto; margin-right: auto;"><img border="0" height="307" src="http://1.bp.blogspot.com/-LE-cs0evhcQ/VIqKvGIBVtI/AAAAAAAAA6M/Y7_dKcICg08/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A26%3A27.png" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 6.</b> Example of an Attention Model on a page</td></tr></tbody></table><div><br /></div><span id="docs-internal-guid-a151ba49-0d50-b49d-dad4-e8c3299922b6"><span id="docs-internal-guid-704b7fa8-3d2c-0727-b62e-9a09a90f347d"></span></span><span id="docs-internal-guid-a151ba49-0d50-b49d-dad4-e8c3299922b6"></span><span id="docs-internal-guid-a151ba49-0d51-4591-bc30-f4bcaf178ac9"></span><span id="docs-internal-guid-a151ba49-0d51-8d0d-69f3-57381be5df4c"></span> <br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">Let's face it, users can only click and act on whatever your algorithm (and other parts of your system) has decided to show them. Of course, what your algorithm decided to show is what it predicted was good for the user. Let's suppose that a new user comes in and we decide to show the user only popular items. The fact that a week later the user has only consumed popular items does not mean that's what the user like. That's the *only* thing she had a chance to consume!</span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">As many (<a href="http://technocalifornia.blogspot.com/2011/09/recommender-problem-presentation.html">including myself</a>) have mentioned in the past, is important to take that into account in your algorithms and try to somehow break this "Curse of the Presentation Bias". Most approaches to addressing this issue are based on the idea that you should "punish" items that were showed to the user but not "clicked on". One way to do so is by implementing some presentation discounting mechanism (see <a href="http://www.cs.ubc.ca/~peil/papers/kdd2014.pdf">this KDD 2014 paper</a> by the LinkedIn folks). </span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">Another way to address the issue is to use viewed but not clicked items as negatives in your training process. This, in principle, makes sense: if a user searched for a query and ended up clicking in result number three it means the first two results were bad and should be treated as negatives... or not? The problem with this is that although the first two items were likely worse than the third one (at least in that particular context), this does not mean they were any worse than item in position 4, let alone item in position 5000, which your original model decided was no good at all. Yes, you want to remove the presentation bias, but not all of it since it responds to some hopefully well-informed decisions your model took in the first place.</span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">So, what can we do? First thing that comes to mind is to introduce some sort of randomization in the original results. This randomization should allow to collect unbiased user feedback so as to whether those items are good or not (see some of the early publications by Thorsten Joachims such as <a href="http://www.cs.cornell.edu/People/tj/publications/radlinski_joachims_06a.pdf">this one</a> or take a look at the idea of <a href="http://www.slideshare.net/tdunning/which-algorithms-really-matter">result dithering</a> proposed by Ted Dunning).</span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">Another better approach is to develop some sort of "attention model" of the user. In this case both clicked and non-clicked items will be weighted by the probability that the user noticed them in the first place depending on their location on the page (see <a href="http://scholar.google.com/citations?user=95PaR-QAAAAJ&hl=en">some of the recent work by Dmitry Lagun</a> for interesting ideas on this area.</span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">Finally, yet another and well established way to address presentation bias is by using some sort of explore/exploit approach, in particular multi-armed bandits. By using a method such as <a href="http://en.wikipedia.org/wiki/Thompson_sampling">Thompson Sampling</a>, you can introduce some form of "randomization" on the items that you are still not sure about, while still exploiting as much as you can from what you already know for sure (see Deepak Argawal's <a href="http://www.ueo-workshop.com/wp-content/uploads/2013/10/UEO-Deepak.pdf">Explore/Exploit approach to recommendations</a> or one of the many publications by <a href="http://www.cs.cornell.edu/People/tj/">Thorsten Joashims</a> for more details on this).</span></span><br /><br /><h2>6. The UI is the only communication channel between the Algorithm and what matters most: the Users</h2><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-IlaQ9wrNQ-w/VIqLXXfAheI/AAAAAAAAA6U/ba9_p58o29I/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A29%3A37.png" style="margin-left: auto; margin-right: auto;"><img border="0" height="246" src="http://4.bp.blogspot.com/-IlaQ9wrNQ-w/VIqLXXfAheI/AAAAAAAAA6U/ba9_p58o29I/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A29%3A37.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 7. </b>The UI is the algorithm's connection point with the user</td></tr></tbody></table><div><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">From the discussion in the previous lesson it should be clear by now how important it is to think about the presentation layer and the user interface in our machine learning algorithmic design. On the one hand, the UI generates all the user feedback that we will use as input to our algorithms. On the other hand, the UI is the only place where our algorithms will be shown. It doesn't matter how smart our ML algorithm is. If the UI hides its results or does not give the user the ability to give some form of feedback, all our efforts on the modeling side will have been in vain.</span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">Also, it is important to understand that a change in the user interface might require a change in the algorithms and vice versa. Just as we learned before that there is an intimate connection between features and models, there is also another to be aware of between the algorithms and the presentation layer.</span></span><br /><br /></div><h2>7. Data and Models are great. You know what is even better? The right evaluation approach.</h2><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-boOR_M7VONg/VIqLwklV2hI/AAAAAAAAA6c/vTcDDGxz-5g/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A30%3A42.png" style="margin-left: auto; margin-right: auto;"><img border="0" height="384" src="http://3.bp.blogspot.com/-boOR_M7VONg/VIqLwklV2hI/AAAAAAAAA6c/vTcDDGxz-5g/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A30%3A42.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 8. </b>Offline/Online Innovation Approach</td></tr></tbody></table><div><br /></div><div><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;">This is probably one of the most important of the lessons in this post. Actually, as I write this I feel that it is a bit unfortunate that this lesson might seem as "just another lesson" hidden in position 7. This should be a good place to stress that these lessons in this post are not sorted from more to less important, they are just grouped in topics or themes.</span><br /><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;">So, yes, as important as all the other discussions about data, models, and infrastructure may be, they are all rather useless if you don't have the right evaluation approach in place. If you don't know how to measure an improvement you might be endlessly spinning your wheels without really getting anywhere. Some of the biggest gains I have seen in practice have indeed come from tuning the metrics to which models were being optimized.</span><br /><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;">Ok, then what is the "right evaluation approach"? Figure 8 illustrates an offline/online approach to innovation that should be a good starting point. Whatever the final goal of your machine learning algorithm is in your product you should think of driving your innovation in two distinct ways: offline and online.</span><br /><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;"><br /></span><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-VTRWeZePLPE/VIqMIiZ60dI/AAAAAAAAA6k/UrX3q2EJhVA/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A32%3A32.png" style="margin-left: auto; margin-right: auto;"><img border="0" height="308" src="http://1.bp.blogspot.com/-VTRWeZePLPE/VIqMIiZ60dI/AAAAAAAAA6k/UrX3q2EJhVA/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A32%3A32.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 9.</b> Offline Evaluation</td></tr></tbody></table><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;">First, you should generate datasets that allow to try different models and features in an offline fashion by following a traditional ML experimentation approach (see Figure 9): You train your model to a training seat, you probably optimize some (hyper)parameters to a validation set, and finally measure some evaluation metrics on a test set. The evaluation metrics in our context are likely to be IR metrics such as precision and recall, ROC curves, or ranking metrics such as <a href="http://en.wikipedia.org/wiki/Discounted_cumulative_gain">NDCG</a>, MRR, or FPC (Fraction of <a href="http://en.wikipedia.org/wiki/Concordant_pair">Concordant Pairs</a>). Note though that the selection of the metric itself has its consequences. Take a look at Figure 10 for an example of how the different ranking metrics weight different ranks being evaluated. In that sense, metrics such as MRR or (especially) NDCG will give much more importance to the head of the ranking, while FPC will be weighting more on the middle of the ranks. The key here is that depending on your application you should choose the right metric.</span><br /><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;"><br /></span><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-4XpWGVRI50M/VIqMNJty8TI/AAAAAAAAA6s/aaTzCltjMNY/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A32%3A48.png" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-4XpWGVRI50M/VIqMNJty8TI/AAAAAAAAA6s/aaTzCltjMNY/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A32%3A48.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig. 10. </b>Importance given to different ranks by typical ranking metrics</td></tr></tbody></table><br /><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;">Offline experimentation is great because once you have the right data and the right metric it is fairly cheap to run many experiments with very few resources. Unfortunately, a successful offline experiment can only be generally used as an indication of a promising approach worth testing online. While most companies are investing in finding better correlation between offline and online results, this is still, generally speaking, an unsolved issue that deserves more research (see <a href="http://dl.acm.org/citation.cfm?id=2488215">this KDD 2013 paper</a>, for example). </span><br /><span style="font-family: inherit; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="white-space: pre-wrap;">In online experimentation the most usual approach is to do A/B testing (other approaches such as <a href="https://support.google.com/analytics/answer/2844870?hl=en">Multiarmed Bandit Testing</a> or <a href="http://www.cs.cornell.edu/people/tj/publications/chapelle_etal_12a.pdf">Interleaved Testing</a> are becoming more popular recently but are beyond the scope of this post). The goal of an A/B test is to measure difference in metrics across statistically identical populations that each experience a different algorithm. As with the offline evaluation process, and perhaps even more here, it is very important to choose the appropriate evaluation metric to make sure that most if not all decisions on the product are data driven. </span><br /><span style="white-space: pre-wrap;"><br /></span><span style="white-space: pre-wrap;">Most people will have a number of different metrics they are tracking in any AB test, but it is important to clearly identify the so-called Overall Evaluation Criteria (OEC). This should be the ultimate metric used for product decisions. In order to avoid noise and make sure the OEC maps well to business success it is better to use a long-term metric (e.g. customer retention). Of course, the issue with that is that you need time, and therefore resources, to evaluate a long-term metric. That is why it is very useful to have short-term metrics that can be used as initial early reads on the tests in order to narrow down worthwhile hypothesis that need to wait until the OEC read is complete.</span><br /><br />If you want more details on the online experimentation piece there are many good reads, starting with the many good articles by Bing's Ronny Kohavi (see <a href="http://ai.stanford.edu/~ronnyk/2013%20controlledExperimentsAtScale.pdf">this</a>, for example).</div><div><span id="docs-internal-guid-91e5a539-3d32-4035-9513-f42bed9adb25"><span style="font-family: inherit;"><span style="line-height: 16px; white-space: pre-wrap;"><br /></span></span></span></div><div class="separator" style="clear: both; text-align: center;"><br /></div><h2>8. Distributing algorithms? Yes, but at what level?</h2><div><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">There always comes a time in the life of a Machine Learning practitioner when you feel the need to distribute your algorithm. Distributing algorithms that require of many resources is a natural thing to do. The issue to consider is at what *level* does it make sense to distribute.</span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">We distinguish three levels of distribution:</span></span><br /><ul><li><span style="font-family: inherit; line-height: 1; white-space: pre-wrap;">Level 1. For each independent subset of the overall data </span></li><li><span style="font-family: inherit; line-height: 1; white-space: pre-wrap;">Level 2. For every combination of the hyperparameters</span></li><li><span style="font-family: inherit; line-height: 1; white-space: pre-wrap;">Level 3. For all partitions in each training dataset</span></li></ul><br />In the first level we may have subsets of the overall data for which we need to (or simply can) train an independently optimized model. A typical example of this situation is when we opt for training completely independent ML models for different regions in the world, different kinds of users, or different languages. In this case, all we need to do is to define completely independent training datasets. Training can then be fully distributed requiring no coordination or data communication.<br /><br />In the second level, we address the issue of how to train several models with different hyperparameter values in order to find the optimal model. Although there are smarter ways to do it, let's for now think of the worst-case grid search scenario. We can definitely train models with different values of the hyperparameters in a completely distributed fashion, but the process does require coordination. Some central location needs to gather results and decide on the next "step" to take. Level 2 requires data distribution, but not sharing since each node will use a complete replica of the original dataset and the communication will happen at the level of the parameters.<br /><br />Finally, in level 3 we address the issue of how to distribute or parallelize model training for a single combination of the hyperparameters. This is a hard problem, but there has been a lot of research put into it. There are different solutions with different pros and cons. You can distribute computation over different machines splitting examples or parameter using, for example, <a href="http://stanford.edu/~boyd/admm.html">ADMM</a>. Recent solutions such as the <a href="http://parameterserver.org/">Parameter Sever</a> promise to offer a generic solution to this problem. Another option is to parallelize on a single multicore machine using algorithms such as <a href="http://www.eecs.berkeley.edu/~brecht/papers/hogwildTR.pdf">Hogwild</a>. Or, you can use the massive array of cores available in GPU cards.<br /><br />As an example of the different approaches you can take to distribute each of the levels, take a look at <a href="http://techblog.netflix.com/2014/02/distributed-neural-networks-with-gpus.html">what we did</a> in our distribution of Artificial Neural Networks over the AWS cloud (see Figure 11 below for an illustration). For Level 1 distribution, we simply used different machine instances over different AWS regions. For Level 2 we used different machine in the same region and a central node for coordination. We used Condor for cluster coordination (although other options such as StarCluster, Mesos, or even Spark) are possible. Finally, for level 3 optimization, we used highly optimized CUDA code on GPUs.<br /><div><span><span style="white-space: pre-wrap;"><br /></span></span></div><span id="docs-internal-guid-2e000fb4-3d33-e866-ae0f-47dcdd6ee409"></span><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-bV484RKiTAc/VIqMnegzc5I/AAAAAAAAA60/jYLfDo-GCro/s1600/DistributedANN-Final.png" style="margin-left: auto; margin-right: auto;"><img border="0" height="267" src="http://1.bp.blogspot.com/-bV484RKiTAc/VIqMnegzc5I/AAAAAAAAA60/jYLfDo-GCro/s1600/DistributedANN-Final.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 11. </b>Distributing ANN over the AWS cloud</td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"><span id="docs-internal-guid-2e000fb4-3d33-e866-ae0f-47dcdd6ee409"></span></div><span id="docs-internal-guid-2e000fb4-3d33-e866-ae0f-47dcdd6ee409"></span><br /><div><span id="docs-internal-guid-2e000fb4-3d33-e866-ae0f-47dcdd6ee409"><span><br /></span></span></div><span id="docs-internal-guid-2e000fb4-3d33-e866-ae0f-47dcdd6ee409"></span></div><h2>9. It pays off to be smart about your Hyperparameters</h2><div><span id="docs-internal-guid-b7ec9469-3d35-8cb0-546e-5738b3abc26a"><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">As already mentioned in the previous lesson, one of the important things you have to do when building your ML system is to tune your hyperparameters. Most, if not all, algorithms will have some hyperparameters that need to be tuned: learning rate in matrix factorization, regularization lambda in logistic regression, number of hidden layers in a neural network, shrinkage in gradient boosted decision trees... These are all parameters that need to be tuned to the validation data.</span></span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="line-height: 16px; white-space: pre-wrap;">Many times you will face situations in which models need to be periodically retrained and therefore hyperparameters need to be at least fine-tuned. This is a clear situation where you need to figure out a way to automatically select the best hyperparameters without requiring a manual check. As a matter of fact, having an automatic hyperparameter selection approach is worthwhile even if all you are doing is the initial experimentation. A fair approach is to try all possible combinations of hyperparameters and pick the one that maximizes a given accuracy metric on the validation set. While this is, generally speaking, a good idea, it might be problematic if implemented directly. The issue is that blindly taking the point that optimizes whatever metric does not take into account the possible noisiness in the process and the metric. In other words, we can't be sure that if point A has an accuracy that is only 1% better than point B, point A is a better operating point than B. </span><br /><span style="line-height: 16px; white-space: pre-wrap;"><br /></span><span style="line-height: 16px; white-space: pre-wrap;">Take a look at Figure 12 below, which illustrates this issue by showing (made up) accuracy results for a model given different values of the regularization parameter. In this particular example the highest accuracy is for no regularization, plus there is a relatively flat plateau region for values of lambda between 0.1 and 100. Blindly taking a value of lambda of zero is generally a bad idea since it points to overfitting (yes, this could be checked by using the test dataset). But, beyond that, going to the "flat region", is it better to stick with the 0.1 value? By looking at the plot I would be inclined to take 100 as the operating point. This point is (a) non-zero, and (b) noise-level different in terms of accuracy from the other non-zero values. So, one possible rule of thumb to use is to keep the highest non-zero value that is noise level different in terms of the optimizing metric from the optimal point.</span><br /><br /></div><div><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-MlCCRurPOMY/VIqM-_W_GNI/AAAAAAAAA68/Yba5QlkSNvM/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A36%3A34.png" style="margin-left: auto; margin-right: auto;"><img border="0" height="401" src="http://2.bp.blogspot.com/-MlCCRurPOMY/VIqM-_W_GNI/AAAAAAAAA68/Yba5QlkSNvM/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A36%3A34.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 12. </b>Example of model accuracy vs. regularization lambda</td></tr></tbody></table><div><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">I should also add that even though in this lesson I have dsf about using a brute-force grid search approach to hyperparameter optimization, there are much better things you can do which are again beyond the scope of this post. If you are not familiar with Bayesian Optimization, start with <a href="http://papers.nips.cc/paper/4522-practical-bayesian-optimization-of-machine-learning-algorithms.pdf">this paper</a> or take a look at <a href="https://github.com/JasperSnoek/spearmint">Spearmint</a> or <a href="http://engineeringblog.yelp.com/2014/07/introducing-moe-metric-optimization-engine-a-new-open-source-machine-learning-service-for-optimal-ex.html">MOE</a>.</span></span><br /><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;"><br /></span></span></div><h2>10. There are things you can do Offline and there are things you can't... and there is Nearline for everything in between</h2><div><span style="line-height: 16px; white-space: pre-wrap;">In the lessons so far we have talked about the importance of data, models, UI, metrics... In this last lesson I thought it was worth to focus on systems and architecture. When the final goal of your ML model is to have impact on a product, you are necessarily going to have to think about the right system architecture. </span><br /><span style="line-height: 16px; white-space: pre-wrap;"><br /></span><span style="line-height: 16px; white-space: pre-wrap;">Figure 13 depicts a three level architecture that can be used as a </span><span style="line-height: 16px; white-space: pre-wrap;">blueprint for any machine learning system that is designed to have a customer impact. The basic idea is that it is important to have different layers in which to trade off latency vs. complexity. Some computations need to be as real-time as possible to quickly respond to user feedback and context. Those are better off in an online setting. On the other extreme, complex ML models that require large amounts of data and lengthy computations are better done in an offline fashion. Finally, there is a Nearline world where operations are not guaranteed to happen in real-time but a best effort is performed to do them as "soon as possible".</span><br /><br /></div><div><span style="font-family: inherit;"><span style="color: #666666; line-height: 1; white-space: pre-wrap;"><br /></span></span></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-ab7QnWsT-GY/VIqNala6WgI/AAAAAAAAA7E/hragQzAHvLI/s1600/MachineLearningArchitecture-v3.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" height="640" src="http://4.bp.blogspot.com/-ab7QnWsT-GY/VIqNala6WgI/AAAAAAAAA7E/hragQzAHvLI/s1600/MachineLearningArchitecture-v3.jpg" width="579" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 13.</b> This three level architecture can be used as a blueprint for machine learning systems that drive customer impact.</td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Interestingly, thinking about these three "<a href="https://gigaom.com/2013/03/28/3-shades-of-latency-how-netflix-built-a-data-architecture-around-timeliness/">shades of latency</a>" also helps breaking down traditional machine learning algorithms into different components that can be executed in different layers. Take matrix factorization as an example. As illustrated in Figure 14, you can decide to do the more time-consuming item factor computation in an offline fashion. Once those item factors are computed, you can compute user factors online (e.g. solving a closed-from least squares formulation) in a matter of milliseconds in an online fashion.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-jk-YlHAYJSs/VIqNpNdllFI/AAAAAAAAA7M/vAzZo_jJrDw/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A39%3A21.png" style="margin-left: auto; margin-right: auto;"><img border="0" height="287" src="http://1.bp.blogspot.com/-jk-YlHAYJSs/VIqNpNdllFI/AAAAAAAAA7M/vAzZo_jJrDw/s1600/Screenshot%2Bfrom%2B2014-12-11%2B22%3A39%3A21.png" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><b>Fig 14. </b>Decomposing matrix factorization into offline and online computation</td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"><br /></div><div><span style="font-family: inherit;"><span style="line-height: 1; white-space: pre-wrap;">If you are interested in this topic take a look at our original <a href="http://techblog.netflix.com/2013/03/system-architectures-for.html">blog post </a>in the Netflix tech blog.</span></span></div><h2>Conclusions</h2><div>The ten lessons in this post illustrate knowledge gathered from building impactful machine learning and general algorithmic solutions. If I had to summarize them in 4 short take away messages those would probably be:</div><div><br /></div><ol id="docs-internal-guid-4adf011f-0d4b-3cf3-4e30-1bc4c93ad704" style="margin-bottom: 0pt; margin-top: 0pt;"><li dir="ltr"><div dir="ltr" style="line-height: 1; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: small;"><span>Be thoughtful about your data</span></span></div></li><li dir="ltr"><div dir="ltr" style="line-height: 1; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: small;"><span>Understand dependencies between data and models</span></span></div></li><li dir="ltr"><div dir="ltr" style="line-height: 1; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: small;"><span>Choose the right metric</span></span></div></li><li dir="ltr"><div dir="ltr" style="line-height: 1; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: small;"><span> Optimize only wha</span></span><span style="font-size: small;"><span>t matters</span></span></div></li></ol><span style="font-size: small;"><br />I hope they are useful to other researchers and practicioners. And, would love to hear about similar or different experiences in building real-life machine learning solutions in the comments. Looking forward to the feedback.</span><br /><h2>Acknowledgments</h2>Most of the above lessons have been learned in close collaboration with my former Algorithms Engineering team at Netflix. In particular I would like to thank <a href="https://twitter.com/JustinBasilico">Justin Basilico</a> for many fruitful conversations, feedback on the original drafts of the slides, and for providing some of the figures in this post.<br /><h2>Original video and slides</h2><br /><br /><div class="separator" style="clear: both; text-align: center;"> </div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div style="text-align: center;"><br /> </div><div style="margin-bottom: 5px; text-align: center;"><b> <a href="https://www.slideshare.net/xamat/10-lessons-learned-from-building-machine-learning-systems" target="_blank" title="10 Lessons Learned from Building Machine Learning Systems">10 Lessons Learned from Building Machine Learning Systems</a> </b> from <b><a href="https://www.slideshare.net/xamat" target="_blank">Xavier Amatriain</a></b> </div>2015-12-16T07:26:46+00:00Xavier AmatriainXavier Amatriain: Introduction to Recommender Systems: A 4-hour lecture
http://technocalifornia.blogspot.com/2014/08/introduction-to-recommender-systems-4.html
<br /><div class="separator" style="clear: both; text-align: center;"><a href="http://mlss2014.com/index.html"><img border="0" height="94" src="http://mlss2014.com/images/mlsslogo.png" width="320" /></a></div><br /><br /><div style="text-align: justify;">A couple of weeks ago, I gave a 4 hour lecture on Recommender Systems at the <a href="http://mlss2014.com/index.html">2014 Machine Learning Summer School at CMU</a>. The school was organized by Alex Smola and Zico Kolter and, judging by the attendance and the quality of the speakers, it was a big success. </div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">This is the outline of my lecture:</div><div style="text-align: justify;"><br /></div><ol style="margin-bottom: 0pt; margin-top: 0pt;"><li><span style="font-family: Arial; line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">Introduction: What is a Recommender System</span></i></span></li><li><span style="font-family: Arial; line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">“Traditional” Methods</span></i></span></li><ol style="margin-bottom: 0pt; margin-top: 0pt;"><li><span style="font-family: Arial; line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">Collaborative Filtering</span></i></span></li><li><span style="font-family: Arial; line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">Content-based Recommendations</span></i></span></li></ol><li><span style="font-family: Arial; line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">"Novel" Methods</span></i></span></li><ol style="margin-bottom: 0pt; margin-top: 0pt;"><li><span style="font-family: Arial; line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">Learning to Rank</span></i></span></li><li><span style="font-family: Arial; line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">Context-aware Recommendations</span></i></span></li><ol style="margin-bottom: 0pt; margin-top: 0pt;"><li><span style="font-family: Arial; line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">Tensor Factorization</span></i></span></li><li><span style="font-family: Arial; line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">Factorization Machines</span></i></span></li></ol><li><span style="font-family: Arial; line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">Deep Learning</span></i></span></li><li dir="ltr" style="background-color: transparent; font-family: Arial; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.04; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><i><span style="color: #999999;">Similarity</span></i></span></div></li><li dir="ltr" style="background-color: transparent; font-family: Arial; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.04; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><i><span style="color: #999999;">Social Recommendations</span></i></span></div></li></ol><li><span style="font-family: Arial; line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">Hybrid Approaches</span></i></span></li><li dir="ltr" style="background-color: transparent; font-family: Arial; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.04; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><i><span style="color: #999999;">A practical example: Netflix </span></i></span></div></li><li dir="ltr" style="background-color: transparent; font-family: Arial; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.04; margin-bottom: 0pt; margin-top: 0pt;"><span style="line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">Conclusions</span></i></span></div></li><li dir="ltr" style="background-color: transparent; font-family: Arial; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"><div dir="ltr" style="line-height: 1.04; margin-bottom: 0pt; margin-top: 0pt;"><span style="line-height: 1.04; white-space: pre-wrap;"><i><span style="color: #999999;">References</span></i></span></div></li></ol><div style="text-align: justify;"><span id="docs-internal-guid-75f3313b-a494-d759-5bfd-3fe0636d864e"><br /></span></div>You can access the slides in Slideshare and the videos in Youtube, but I thought it would make sense to gather both here and link them together.<br /><br />Here are the slides:<br /><br /><div style="text-align: center;"> </div><div style="margin-bottom: 5px;"><strong> <a href="https://www.slideshare.net/xamat/recommender-systems-machine-learning-summer-school-2014-cmu" target="_blank" title="Recommender Systems (Machine Learning Summer School 2014 @ CMU)">Recommender Systems (Machine Learning Summer School 2014 @ CMU)</a> </strong> from <strong><a href="http://www.slideshare.net/xamat" target="_blank">Xavier Amatriain</a></strong> <br /><br />Here is the first session (2 hours):</div><div class="separator" style="clear: both; text-align: center;"></div><br /><br />Here is the second session (2 hours):<br /><br /><div class="separator" style="clear: both; text-align: center;"></div><br />2014-08-05T05:23:34+00:00Xavier AmatriainDavid Garcia: Pythonic access to audio files: python-wavefile
http://canvoki.net/coder/blog/2014-06-30-python-wavefile.html
<div style="float: right; text-align: right;"><img src="http://canvoki.net/coder/media/images/python-wavefile-banner.png" style="width: 90%; display: inline;" /></div>
<p>Last week,
<a href="http://canvoki.net/coder/blog/(https://github.com/vokimon/python-wavefile)">python-wavefile</a>
received a pull request from the <a href="https://github.com/j3ffhubb/pydaw">PyDAW</a> project
to make it compatible with Python3.
So, I awaked the project to pull the contributions and addressing some of the old pending tasks.</p>
<p>I did not realize <a href="https://github.com/vokimon/python-wavefile">python-wavefile</a>
got more relevance than most of my github projects:
Other people, not just me, are actually using it, and that’s cool.
So I think I owe the project a blog entry… and maybe a logo.</p>
<p><strong>python-wavefile</strong> is a Python module to read and write audio files in a pythonic way.
Instead of just exposing the C <span class="caps">API</span> of the powerful Eric De Castro Lopo’s <a href="http://canvoki.net/coder/blog/(http://www.mega-nerd.com/libsndfile/)">libsndfile</a>,
it enables common <strong>Python idioms</strong> and <strong>numpy bridging</strong> for signal processing.
There are many Python modules around wrapping libsndfile including an standard one.
At the end of the article I do a quick review of them and justify why i did yet-another libsndfile Python wrapper.</p>
<h2 id="history">History</h2>
<p>This module was born to cover the needs I had while doing research for my PhD thesis on 3D audio.
I needed
<strong>floating point samples</strong> and
<strong>multi-channel formats</strong> for Higher Order Ambisonics and multi-speaker mix-down.
I also needed <strong>efficient block processing</strong>,
as well as the inefficient, but sometimes <strong>convenient</strong>, Matlab-like load-it-all functionality.</p>
<p>This is why I proposed Xavi Serra, when he was starting
<a href="http://lac.linuxaudio.org/2013/download/lac2013_ipyclam-presentation.pdf">his Master Thesis</a>,
a warm-up exercise:
Mocking up Python bindings for the libsndfile library using different methods:
Cython, CPython module, Boost, CTypes, <span class="caps">SIP</span>…
That exercise resulted in several mock-ups for each binding method,
and an almost full implementation using CPython,
based on the double layer strategy Xavi finally used for iPyCLAM:
A lower narrow layer making the C <span class="caps">API</span> available to Python as is, and a user layer adding the Python sugar.</p>
<p>As we evolved the wrapper towards the user layer we wanted,
CPython code became too complex.
So I created python-wavefile by reimplementing the user <span class="caps">API</span>
we defined with Xavier Serra but relying on the C-<span class="caps">API</span> wrapping
defined in libsndfile-ctypes.</p>
<h2 id="python-wave-the-official-api-and-the-root-of-all-evil">Python-wave, the official <span class="caps">API</span> and the root of all evil</h2>
<p>Why do we do that?
The root of all evil is <a href="https://docs.python.org/3/library/wave.html">Python official module</a> to deal with wave files.
It is based on <a href="http://canvoki.net/coder/blog/(http://www.mega-nerd.com/libsndfile/)">libsndfile</a> as well, but the Python <span class="caps">API</span> is a crap, a real crap:</p>
<ul>
<li>:-) As standard lib it is available on every Python install, but…</li>
<li>:-( It has <strong>nasty accessors</strong> like <code>getcomptype</code>, <code>getsampwidth</code>…<ul>
<li>Names with a hard to read/remember combination of shorts</li>
<li>Using getters instead of properties</li>
</ul>
</li>
<li>:-( It just opens <span class="caps">WAV</span> files, and none of the <a href="http://www.mega-nerd.com/libsndfile/#Features">many formats</a> libsndfile supports</li>
<li>:-( It just opens Mono and Stereo audio.</li>
<li>:-( It just opens some limited encodings.<ul>
<li><a href="http://bugs.python.org/issue1144504">A patch to implement floating point samples</a> was rejected because… well, who knows</li>
</ul>
</li>
<li>:-( Data is passed as coded byte strings.<ul>
<li>On writting, users are responsable of encoding samples which is a low level and error prone task.</li>
<li>Even worse, on reading, users have to implement decoding for every kind of encoding available.</li>
<li>Libsndfile actually does all this stuff for you, so why the hell to use the raw interface?</li>
</ul>
</li>
<li>:-( It ignores Python constructs and idioms:<ul>
<li>Generators to access files progressively in iterations</li>
<li>Context managers to deal safely with file resources</li>
<li>Properties instead of getters and setters</li>
</ul>
</li>
<li>:-( It allocates a new data block for each block you read, which is a <strong>garbage collector nightmare</strong>.</li>
<li>:-( It has no support for <code>numpy</code><ul>
<li>A core lib cannot have a dependency on numpy but it is quite convenient feature to have to perform signal processing</li>
</ul>
</li>
</ul>
<p>Because of this, many programmers built their own libaudiofile wrapper
but most of them fail for some reason, to fulfill the interface I wanted.
Instead of reinventing the wheel I reused design and even code from others.
At the end of the article I place an extensive list of such alternatives and their strong and weak points.</p>
<h2 id="the-api-by-example">The <span class="caps">API</span> by example</h2>
<p>Let’s introduce the <span class="caps">API</span> with some examples.</p>
<p>To try the examples you can install the module from <a href="https://pypi.python.org/pypi/wavefile/">PyPi</a> repositories using the <code>pip</code> command.</p>
<div class="codehilite"><pre><span class="nv">$ </span>pip install wavefile
</pre></div>
<p>Notes for Debian/Ubuntu users:</p>
<ul>
<li>Use <code>sudo</code> or <code>su</code> to get administrative rights</li>
<li>If you want to install it for Python3 use <code>pip3</code> instead</li>
</ul>
<h3 id="writting-example">Writting example</h3>
<p>Let’s create an stereo <span class="caps">OGG</span> file with some metadata and a synthesized sound inside:</p>
<div class="codehilite"><pre><span class="kn">from</span> <span class="nn">wavefile</span> <span class="kn">import</span> <span class="n">WaveWriter</span><span class="p">,</span> <span class="n">Format</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span>
<span class="k">with</span> <span class="n">WaveWriter</span><span class="p">(</span><span class="s">'synth.ogg'</span><span class="p">,</span> <span class="n">channels</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">format</span><span class="o">=</span><span class="n">Format</span><span class="o">.</span><span class="n"><span class="caps">OGG</span></span><span class="o">|</span><span class="n">Format</span><span class="o">.</span><span class="n"><span class="caps">VORBIS</span></span><span class="p">)</span> <span class="k">as</span> <span class="n">w</span> <span class="p">:</span>
<span class="n">w</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="s">"Some Noise"</span>
<span class="n">w</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">artist</span> <span class="o">=</span> <span class="s">"The Artists"</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">zeros</span><span class="p">((</span><span class="mi">2</span><span class="p">,</span><span class="mi">512</span><span class="p">),</span> <span class="n">np</span><span class="o">.</span><span class="n">float32</span><span class="p">)</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span> <span class="p">:</span>
<span class="c"># Synthesize a kind of triangular sweep in one channel</span>
<span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">,:]</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">512</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">np</span><span class="o">.</span><span class="n">float32</span><span class="p">)</span><span class="o">%</span><span class="mi">512</span><span class="o">/</span><span class="mi">512</span><span class="p">)</span>
<span class="c"># And a squared wave on the other</span>
<span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">512</span><span class="o">-</span><span class="n">x</span><span class="p">:]</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="p">,:</span><span class="mi">512</span><span class="o">-</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
<span class="n">w</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</pre></div>
<h3 id="playback-example-using-pyaudio">Playback example (using pyaudio)</h3>
<p>Let’s playback a command line specified audio file and see its metadata and format.</p>
<div class="codehilite"><pre><span class="kn">import</span> <span class="nn">pyaudio</span><span class="o">,</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">wavefile</span> <span class="kn">import</span> <span class="n">WaveReader</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">pyaudio</span><span class="o">.</span><span class="n">PyAudio</span><span class="p">()</span>
<span class="k">with</span> <span class="n">WaveReader</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="k">as</span> <span class="n">r</span> <span class="p">:</span>
<span class="c"># Print info</span>
<span class="k">print</span> <span class="s">"Title:"</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">title</span>
<span class="k">print</span> <span class="s">"Artist:"</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">artist</span>
<span class="k">print</span> <span class="s">"Channels:"</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">channels</span>
<span class="k">print</span> <span class="s">"Format: 0x</span><span class="si">%x</span><span class="s">"</span><span class="o">%</span><span class="n">r</span><span class="o">.</span><span class="n">format</span>
<span class="k">print</span> <span class="s">"Sample Rate:"</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">samplerate</span>
<span class="c"># open pyaudio stream</span>
<span class="n">stream</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">open</span><span class="p">(</span>
<span class="n">format</span> <span class="o">=</span> <span class="n">pyaudio</span><span class="o">.</span><span class="n">paFloat32</span><span class="p">,</span>
<span class="n">channels</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">channels</span><span class="p">,</span>
<span class="n">rate</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">samplerate</span><span class="p">,</span>
<span class="n">frames_per_buffer</span> <span class="o">=</span> <span class="mi">512</span><span class="p">,</span>
<span class="n">output</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span>
<span class="c"># iterator interface (reuses one array)</span>
<span class="c"># beware of the frame size, not always 512, but 512 at least</span>
<span class="k">for</span> <span class="n">frame</span> <span class="ow">in</span> <span class="n">r</span><span class="o">.</span><span class="n">read_iter</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">512</span><span class="p">)</span> <span class="p">:</span>
<span class="n">stream</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">frame</span><span class="p">,</span> <span class="n">frame</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">"."</span><span class="p">);</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
<span class="n">stream</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</pre></div>
<h3 id="processing-example">Processing example</h3>
<p>Let’s process some file by lowering the volume and changing the title.</p>
<div class="codehilite"><pre><span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">wavefile</span> <span class="kn">import</span> <span class="n">WaveReader</span><span class="p">,</span> <span class="n">WaveWriter</span>
<span class="k">with</span> <span class="n">WaveReader</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="k">as</span> <span class="n">r</span> <span class="p">:</span>
<span class="k">with</span> <span class="n">WaveWriter</span><span class="p">(</span>
<span class="s">'output.wav'</span><span class="p">,</span>
<span class="n">channels</span><span class="o">=</span><span class="n">r</span><span class="o">.</span><span class="n">channels</span><span class="p">,</span>
<span class="n">samplerate</span><span class="o">=</span><span class="n">r</span><span class="o">.</span><span class="n">samplerate</span><span class="p">,</span>
<span class="p">)</span> <span class="k">as</span> <span class="n">w</span> <span class="p">:</span>
<span class="n">w</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">title</span> <span class="o">+</span> <span class="s">" (dull version)"</span>
<span class="n">w</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">artist</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">artist</span>
<span class="k">for</span> <span class="n">data</span> <span class="ow">in</span> <span class="n">r</span><span class="o">.</span><span class="n">read_iter</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">512</span><span class="p">)</span> <span class="p">:</span>
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">"."</span><span class="p">);</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
<span class="n">w</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="o">.</span><span class="mi">8</span><span class="o">*</span><span class="n">data</span><span class="p">)</span>
</pre></div>
<p><code>read_iter</code> simplifies the code by transparently:</p>
<ul>
<li>allocating the data block for you,</li>
<li>reusing such block for each read and thus reducing the memory overhead, and</li>
<li>returning a slice of it when the last incomplete block arrives.</li>
</ul>
<h3 id="masochist-example">Masochist example</h3>
<p>If you like you can still do things by hand using a more C-ish <span class="caps">API</span>:</p>
<div class="codehilite"><pre><span class="kn">import</span> <span class="nn">sys</span><span class="o">,</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">wavefile</span> <span class="kn">import</span> <span class="n">WaveReader</span><span class="p">,</span> <span class="n">WaveWriter</span>
<span class="k">with</span> <span class="n">WaveReader</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="k">as</span> <span class="n">r</span> <span class="p">:</span>
<span class="k">with</span> <span class="n">WaveWriter</span><span class="p">(</span>
<span class="s">'output.wav'</span><span class="p">,</span>
<span class="n">channels</span><span class="o">=</span><span class="n">r</span><span class="o">.</span><span class="n">channels</span><span class="p">,</span>
<span class="n">samplerate</span><span class="o">=</span><span class="n">r</span><span class="o">.</span><span class="n">samplerate</span><span class="p">,</span>
<span class="p">)</span> <span class="k">as</span> <span class="n">w</span> <span class="p">:</span>
<span class="n">w</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">title</span> <span class="o">+</span> <span class="s">" (masochist)"</span>
<span class="n">w</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">artist</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">artist</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">buffer</span><span class="p">(</span><span class="mi">512</span><span class="p">)</span> <span class="c"># equivalent to: np.empty((r.channels,512), np.float32, order='F')</span>
<span class="n">nframes</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">while</span> <span class="n">nframes</span> <span class="p">:</span>
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">"."</span><span class="p">);</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
<span class="n">w</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="o">.</span><span class="mi">8</span><span class="o">*</span><span class="n">data</span><span class="p">[:,:</span><span class="n">nframes</span><span class="p">])</span>
<span class="n">nframes</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</pre></div>
<p>Notice that with <code>read</code> you have to reallocate the data yourself,
the loop structure is somewhat more complex with duplicated read inside and outside the loop.
You also have to slice to the actual number of read frames since
the last block usually does not have the size you asked for.</p>
<p>The <span class="caps">API</span> uses channel as the first index for buffers.
This is convenient because usually processing splits channels first.
But audio files (<span class="caps">WAV</span>) interleaves samples for different channels in the same frame:</p>
<div class="codehilite"><pre><span class="n">f1ch1</span> <span class="n">f1ch2</span> <span class="n">f2ch1</span> <span class="n">f2ch2</span> <span class="n">f3ch1</span> <span class="n">f3ch2</span> <span class="p">...</span>
</pre></div>
<p>Reads are optimized by using a read buffer with Fortran order (F).
Numpy handles the indexing transparently but for the read buffer,
and just for the read buffer we recommend to use the <code>buffer()</code> method.
That’s not needed for the rest of buffers, for example, for writting
and you don’t have to worry at all if you are using the <code>read_iter</code> <span class="caps">API</span>.</p>
<h3 id="load-and-save-it-all-interface">Load and save it all interface</h3>
<p>This interface is <strong>not recommended for efficient processing</strong>,
because it loads all the audio data in memory at once,
but is sometimes convenient in order to have some code quickly working.</p>
<div class="codehilite"><pre><span class="kn">import</span> <span class="nn">wavefile</span>
<span class="n">samplerate</span><span class="p">,</span> <span class="n">data</span> <span class="o">=</span> <span class="n">wavefile</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="s">"synth.ogg"</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">,:]</span> <span class="c"># invert channels</span>
<span class="n">wavefile</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s">"output.flac"</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">samplerate</span><span class="p">)</span>
</pre></div>
<h2 id="new-introduced-features">New introduced features</h2>
<h3 id="python-3-support">Python 3 support</h3>
<p>That was the pull request from Jeff Hugges of the PyDAW project.
Thanks a lot for the patches!</p>
<p>We managed to make Python 3 code to be also compatible with Python 2.
So now the same code base works on both versions and passes the same tests.</p>
<h3 id="unicode-in-paths-and-tags">Unicode in paths and tags</h3>
<p>Besides Python3 compatibility, now the <span class="caps">API</span> deals transparently with Unicode strings
both for file names and text tags such as title, artist…</p>
<p>If you encode the string before passing it to the <span class="caps">API</span>, and pass it as a byte string,
the <span class="caps">API</span> will take that encoding with no question and use it.
More safe is just passing the unicode string (unicode in Py2 and str in Py3).
In that case the <span class="caps">API</span> encodes or decodes the string transparently.
In the case of filenames, it uses the file system default encoding available to Python as <code>sys.getfilesystemencoding()</code>.
In the case of text tags, it will use <span class="caps">UTF</span>-8 which is the standard for Vorbis based files (ogg, flac…).</p>
<p><span class="caps">WAV</span>’s and <span class="caps">AIFF</span> standard just specifies about <span class="caps">ASCII</span> strings and I had my concerns about using <span class="caps">UTF</span>-8 there.
After <a href="https://github.com/erikd/libsndfile/issues/67">a discussion</a> with Eric de Castro,
we settled that <span class="caps">UTF</span>-8 is a safe option for reading and a nice one to push as de facto standard,
but I am still not confident about the later.
The alternative would have been raise a text encoding exception whenever a non <span class="caps">ASCII</span> character is written
to a <span class="caps">WAV</span>/<span class="caps">AIFF</span>.
I am still open to further arguments.</p>
<h3 id="seek-seek-seek">Seek, seek, seek</h3>
<p>I also added <span class="caps">API</span> to seek within the file.
This enables a feature a user asked like reseting the file reading and being able to loop.
I was uncertain about libsndfile behaviour on seek.
Now such behaviour is engraved on <span class="caps">API</span> unit tests:</p>
<ul>
<li>Seeks can be a positive or negative number of frames from a reference frame</li>
<li>Frames are as many samples as channels, being a sample a digitally encoded audio level</li>
<li>The reference point for the seeking can be the beginning (<span class="caps">SET</span>), the end (<span class="caps">END</span>) or the current next sample to be read (<span class="caps">CUR</span>)<ul>
<li>That is, if your last read was a 10 frame block starting at 40, your current seek reference is 50</li>
</ul>
</li>
<li>Seek returns the new frame position to be read if the jump is successful or -1 if not.</li>
<li>Jumps to the first frame after the last frame do not fail, even though that frame does not exist.</li>
<li><span class="caps">EOF</span> status resets whenever you successfully seek</li>
</ul>
<h2 id="why-yet-another">Why yet another…</h2>
<p>A list of alternative implementations follow.</p>
<h3 id="official-python-wave">Official python-wave</h3>
<p>Nothing to see. It is crap.</p>
<h3 id="scikitsaudiolab">scikits.audiolab</h3>
<ul>
<li>Author: David Cournapeau</li>
<li>Web: <a href="http://cournape.github.io/audiolab/">http://cournape.github.io/audiolab/</a></li>
<li>PyPi: <a href="https://pypi.python.org/pypi/scikits.audiolab/">https://pypi.python.org/pypi/scikits.audiolab/</a></li>
<li>Source: <code>git clone https://github.com/cournape/audiolab</code></li>
<li>Wrap: Cython</li>
<li>:-) Property accessors to format metadata and strings</li>
<li>:-) Matlab like functions</li>
<li>:-) Block processing</li>
<li>:-) Numpy integration</li>
<li>:-) Enumerable formats</li>
<li>:-( Not in-place read (generates a numpy array for each block)</li>
<li>:-( No context managers</li>
<li>:-| Part of a huge library (no dependencies, though)</li>
</ul>
<h3 id="ewave">ewave</h3>
<ul>
<li>Author: C Daniel Meliza</li>
<li>Web: <a href="https://github.com/melizalab/py-ewave">https://github.com/melizalab/py-ewave</a></li>
<li>PyPi:<a href="https://pypi.python.org/pypi/ewave">https://pypi.python.org/pypi/ewave</a></li>
<li>Source: <code>git clone git@github.com:melizalab/py-ewave.git</code></li>
<li>Wrap: Pure Python (not based on libsndfile)</li>
<li>:-( Just <span class="caps">WAV</span>’s and limited encodings (no 24bits)</li>
<li>:-) Support for floating point encodings, multichannel, </li>
<li>:-) Memory mapping for long files</li>
<li>:-) Numpy support</li>
<li>:-) Context managers</li>
</ul>
<h3 id="pysndfile-savanah">pysndfile (savanah)</h3>
<ul>
<li>Author: ???</li>
<li>Web: <a href="http://savannah.nongnu.org/projects/pysndfile/">http://savannah.nongnu.org/projects/pysndfile/</a></li>
<li>Wrap: Swig</li>
<li>:-( Ugly: Uses a similar metadata <span class="caps">API</span> than python-wave</li>
<li>:-( Unusable: unfinished implementation, empty read/write methods in wrapper!</li>
<li>:-( Unmaintained since 2006</li>
</ul>
<h3 id="libsndfile-python">libsndfile-python</h3>
<ul>
<li>Author: Hedi Soula (current maintainer) / Rob Melby (original)</li>
<li>Web: <a href="http://code.google.com/p/libsndfile-python/">http://code.google.com/p/libsndfile-python/</a></li>
<li>Source: <code>svn checkout http://libsndfile-python.googlecode.com/svn/trunk/ libsndfile-python</code></li>
<li>Wrap: CPython</li>
<li>:-) NumPy</li>
<li>:-( Not in-place read (generates a numpy array for each block)</li>
<li>:-( Some edges are not that pythonic</li>
<li>:-) Implements ‘command’ sndfile interface</li>
</ul>
<h3 id="libsndfile-ctypes">libsndfile-ctypes</h3>
<ul>
<li><a href="http://code.google.com/p/pyzic/wiki/LibSndFilectypes">http://code.google.com/p/pyzic/wiki/LibSndFilectypes</a></li>
<li>Author: Timothe Faudot</li>
<li>Source: <code>svn checkout http://pyzic.googlecode.com/svn/trunk/libsndfile-ctypes</code></li>
<li>Wrap: CTypes</li>
<li>:-) no CPython module compilation required</li>
<li>:-) NumPy</li>
<li>:-) Context managers!</li>
<li>:-) Property accessors for format metadata and strings</li>
<li>:-( Not inplace read (creates an array every block read)</li>
<li>:-) No property accessors for strings</li>
<li>:-( No generator idiom</li>
<li>:-( Windows only setup</li>
<li>:-( Text tags not as properties</li>
<li>:-( Long access to constants (scoping + prefixing)</li>
<li>:-( Single object mixing read and write <span class="caps">API</span>’s</li>
</ul>
<h3 id="python-wavefile">python-wavefile</h3>
<p>That’s the one. I used the implementation layer from libsndfile-ctypes.
I really liked the idea of having a direct C mapping without
having to compile a CPython module,
and how nicely the numpy arrays were handled by CTypes.
Then, over that implementation layer,
I added a user level <span class="caps">API</span> implementing pythonic interface
including those supported by other wrappers and the new ones.</p>
<ul>
<li><a href="https://github.com/vokimon/python-wavefile">https://github.com/vokimon/python-wavefile</a></li>
<li>Author: David Garcia Garzon (with code from all the above)</li>
<li>Source: <code>git clone git@github.com:vokimon/python-wavefile.git</code></li>
<li>PyPi: <code>wavefile</code></li>
<li>Wrap: CTypes</li>
<li>:-) Property accessors to format metadata and strings</li>
<li>:-) Dual interface: matlab like and <span class="caps">OO</span> block processing</li>
<li>:-) No CPython module compilation required</li>
<li>:-) NumPy</li>
<li>:-) Context managers!</li>
<li>:-) Pythonic block iteration</li>
<li>:-) Reuses data blocks avoiding garbage collector nigthmares</li>
<li>:-) Matlab load-all interface</li>
<li>:-) Unicode integration</li>
<li>:-) Works in Windows, Linux and Mac</li>
<li>:-) Python 2 and Python 3 support</li>
<li>:-( Command <span class="caps">API</span> not implemented</li>
<li>:-( No simultaneous Read/Write mode</li>
<li>:-( No writting seek</li>
<li>:-( No format enumeration (yet!)</li>
<li>:-( Does not accept single dimensional arrays (nuisance)</li>
</ul>
<h3 id="other-wrappers-i-found-afterwards-and-i-didnt-check">Other wrappers I found afterwards and I didn’t check</h3>
<p>Yet to be reviewed:</p>
<ul>
<li>pysndfile (<span class="caps">IRCAM</span>)<ul>
<li>Author: Axel Roebel (<span class="caps">IRCAM</span>)</li>
<li>Web: <a href="http://forge.ircam.fr/p/pysndfile/">http://forge.ircam.fr/p/pysndfile/</a></li>
<li>PyPi: <a href="https://pypi.python.org/pypi/pysndfile">https://pypi.python.org/pypi/pysndfile</a></li>
<li>Source: No git/svn, Pyx file missing in the tarball</li>
<li>Wrap: Cython</li>
<li>:-) Command <span class="caps">API</span> implemented</li>
<li>:-) Format enumeration</li>
<li>:-) Interface to disable clipping</li>
<li>:-) NumPy</li>
<li>:-( Method read returns a new buffer</li>
</ul>
</li>
<li>sndfile.io<ul>
<li>Author: Eduardo Moguillansky</li>
<li>Web: <a href="https://github.com/gesellkammer/sndfileio">https://github.com/gesellkammer/sndfileio</a></li>
<li>PyPi: <a href="https://pypi.python.org/pypi/sndfileio/">https://pypi.python.org/pypi/sndfileio/</a></li>
<li>Wrap: Relies on scikits.audiolab but, when missing, implements in pure Python simple <span class="caps">WAV</span> and <span class="caps">AIFF</span> formats</li>
<li>:-) Iterator idiom</li>
<li>:-) Plugin abstraction (could eventually allow <span class="caps">MP3</span> using other backends than libsndfile)</li>
<li>:-( Creates buffer per read (just like audiolab does)</li>
</ul>
</li>
<li>PySoundFile<ul>
<li><a href="https://github.com/bastibe/PySoundFile">https://github.com/bastibe/PySoundFile</a> (First commit Aug 2013)</li>
<li>Numpy: </li>
<li>Wrap: <span class="caps">CFFI</span></li>
</ul>
</li>
</ul>2014-06-30T00:00:00+00:00VokimonDavid Garcia: python-wavefile
http://canvoki.net/coder/blog/2014-06-27-python-wavefile.html
<div style="float: right; text-align: right;">
<img src="http://canvoki.net/coder/media/images/logo-git-mediawiki.png" style="display: inline;" />
</div>
<p>Many posts in this blog talk about <a href="http://canvoki.net/coder/blog/(http://canvoki.net/wiko/)">WiKo</a>, <a href="http://canvoki.net/coder/blog/(http://hyde.github.io/)">Hyde</a>, <a href="http://canvoki.net/coder/blog/(http://johnmacfarlane.net/pandoc/)">pandoc</a>…
Solutions we can use to edit wiki like pages as plain text files,
so that I can edit them with my preferred editor (vim),
do site wide search and replace,
track revisions using a version control system such subversion or git,
and reuse the same content to generate multiple media: pdf documents, web pages…</p>
<p>After that Grial Quest I have some solutions that works for me.
Indeed I am writting this entry using MarkDown which turns into a web page by means of Hyde.
But, meanwhile, some of the projects I am involved in already use some kind of traditional wiki system,
and most of them use <a href="http://canvoki.net/coder/blog/(http://mediawiki.org/)">Mediawiki</a>.</p>
<p>Lucky for me,
this week, I have come across a useful git extension.
It git clones the content of a Mediawiki site as it were a git remote repository
so that you can pull revisions into your hard drive,
edit them and push them back into the wiki.</p>
<h2 id="a-quick-tutorial">A quick tutorial</h2>
<p>You can install it on debian/ubuntu with:</p>
<div class="codehilite"><pre> <span class="n">sudo</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="n">install</span> <span class="n">git</span><span class="o">-</span><span class="n">mediawiki</span>
</pre></div>
<p>Once you do that you can execute:</p>
<div class="codehilite"><pre> <span class="n">git</span> <span class="n">clone</span> <span class="n">mediawiki</span><span class="o">::</span><span class="n">http</span><span class="o">:</span><span class="c1">//clam-project.org/w clam-wiki</span>
</pre></div>
<p>Since 3k4 revisions we have in <span class="caps">CLAM</span> are a quite long download and the wiki api and the
server are quite slow, you can avoid the history with:</p>
<div class="codehilite"><pre> <span class="n">git</span> <span class="n">clone</span> <span class="o">-</span><span class="n">c</span> <span class="n">remote</span><span class="p">.</span><span class="n">origin</span><span class="p">.</span><span class="n">shallow</span><span class="o">=</span><span class="nb">true</span> <span class="n">mediawiki</span><span class="o">::</span><span class="n">http</span><span class="o">:</span><span class="c1">//clam-project.org/w clam-wiki</span>
</pre></div>
<p>Before you push back you must set the wiki user.</p>
<div class="codehilite"><pre> <span class="n">git</span> <span class="n">config</span> <span class="n">remote</span><span class="p">.</span><span class="n">origin</span><span class="p">.</span><span class="n">mwLogin</span> <span class="n">MyUser</span>
</pre></div>
<p>git-mediawiki stores git-commit to mediawiki-revision mappings in a parallel branch.</p>
<h2 id="as-migration-path-way">As migration path way</h2>
<p>This extension is not just useful to edit MediaWiki as it where a git remote repository.</p>
<p>It is a nice path to move your wiki to a different system like Hyde,
by turning the pages into markdown with pandoc.</p>
<div class="codehilite"><pre><span class="k">for </span>mwfile in *.mw
<span class="k">do</span>
<span class="k"> </span>pandoc -f mediawiki -o <span class="k">$(</span>basename <span class="nv">$mwfile</span> .mw<span class="k">)</span>.md <span class="nv">$mwfile</span>
<span class="k">done</span>
</pre></div>
<h2 id="the-wish-list">The wish list</h2>
<p>The tool is quite useful by itself, but there are some edges that could be improved (bug reports linked):</p>
<ul>
<li><a href="https://github.com/moy/Git-Mediawiki/issues/22">Attachments are not included.</a>
So if you have, for instance, images, you won’t have them in local.</li>
<li><strong>Cloning, pulling and pushing are slow.</strong>
Those are the operations that interact with the remote MediaWiki.
All the revision handling intelligence happens at users computer,
so git-mediawiki has to download a lot of information from mediawiki previously to do any action.
MediaWiki <span class="caps">API</span> entry points are not designed with those use cases in mind.</li>
<li><a href="https://github.com/moy/Git-Mediawiki/issues/21">Supages do not generate directories.</a>
For instance, if you have a wiki page named <code>Devel/ToDo</code>, which is a subpage of <code>Devel</code>,
instead of generating a folder <code>Devel</code> and a <code>ToDo.mw</code> file inside,
it replaces the slash by <code>%2F</code>, <code>Devel%2FToDo.mw</code>, which looks quite unreadable when you list the files.</li>
</ul>
<p>It is a pity, that git-mediawiki is written in Perl instead of Python.
If it were written in Python I would be fixing those bugs right now :-)</p>2014-06-27T00:00:00+00:00VokimonXavier Amatriain: Blog posts and Summer gigs
http://technocalifornia.blogspot.com/2014/06/blog-posts-and-summer-gigs.html
I have recently heard complaints that this blog is <a href="http://www.stackdriver.com/top-devops-influencers-blogs-follow/">rather quiet lately</a>. I agree. I have definitely been focused on publishing through other sources and have found little time to write interesting things here. On the one hand, I find twitter ideal for communicating quick and short ideas, thoughts, or pointers. You should definitely <a href="http://www.twitter.com/xamat">follow me</a> there if you want to keep up to date. On the other hand, I have published a couple of posts on the <a href="http://techblog.netflix.com/">Netflix Techblog</a>. A few months ago we published a post describing <a href="http://techblog.netflix.com/2013/03/system-architectures-for.html">our three-tier system architecture</a> for personalization and recommendations. More recently we described our implementation of <a href="http://techblog.netflix.com/2014/02/distributed-neural-networks-with-gpus.html">distributed Neural Networks</a> using GPUs and the AWS cloud.<br /><br />The other thing I continue on doing very often is give talks of our work at different events and venues. In the last few months, for instance, I have given talks at LinkedIn, Facebook, and <a href="http://i.stanford.edu/infoseminar/">Stanford</a>.<br /><br />This week I gave a talk and attended the Workshop on Algorithms for Modern Massive Datasets (<a href="http://mmds-data.org/">MMDS</a>). This is a very interesting workshop organized by <a href="http://www.stat.berkeley.edu/~mmahoney/">Michael Mahoney</a> every two years. It brings together a diverse crowd of people, from theoretical physicist and statisticians to industry practicioners. All of them are united by their work on large scale data-driven algorithms. You can find the slides of my presentation <a href="http://www.slideshare.net/xamat/mmds-2014-talk-distributing-ml-algorithms-from-gpus-to-the-cloud">here</a>.<br /><br />So, what is next? If you want to catch some of my future talks, I will be giving a couple of public ones in the next few months.<br /><br />First, I will be lecturing in the Machine Learning Summer School (<a href="http://www.mlss2014.com/">MLSS</a>) at CMU in early July. I am really looking forward to joining such a great least of speakers and visiting Pittsburgh for the first time. I will be lecturing on Recommendation Systems and Machine Learning Algorithms for Collaborative Filtering.<br /><br />Late August I will be giving a 3 hour long <a href="http://www.kdd.org/kdd2014/tutorials.html">Tutorial</a> at KDD in New York. The tutorial is entitled "The Recommender Problem Revisited" and I will be sharing stage with <a href="http://www.cdm.depaul.edu/people/pages/facultyinfo.aspx?fid=653">Bamshad Mobasher</a>.<br /><br />Finally, I was recently notified that a shorter version of the same tutorial has been accepted at <a href="http://recsys.acm.org/recsys14/">Recsys</a>, which this year is held in the Silicon Valley.<br /><br />I look forward to meeting many of you in any of these events. Don't hesitate to ping me if you will be attending.<br /><br />2014-06-20T21:03:33+00:00Xavier AmatriainDavid Garcia: Command of the day: git-mediawiki
http://canvoki.net/coder/blog/2014-06-14-command-of-the-day-git-mediawiki.html
<div style="display: none;"><div style="float: right;">
<img src="http://canvoki.net/coder/media/images/logo-git-mediawiki.png" style="display: inline;" />
</div></div>
<div style="float: right; text-align: right;">
<img src="http://canvoki.net/coder/media/images/logo-git-mediawiki.png" style="display: inline;" />
</div>
<p>Many posts in this blog talk about <a href="http://canvoki.net/coder/blog/(http://canvoki.net/wiko/)">WiKo</a>, <a href="http://canvoki.net/coder/blog/(http://hyde.github.io/)">Hyde</a>, <a href="http://canvoki.net/coder/blog/(http://johnmacfarlane.net/pandoc/)">pandoc</a>…
Solutions we can use to edit wiki like pages as plain text files,
so that I can edit them with my preferred editor (vim),
do site wide search and replace,
track revisions using a version control system such subversion or git,
and reuse the same content to generate multiple media: pdf documents, web pages…</p>
<p>After that Grial Quest I have some solutions that works for me.
Indeed I am writting this entry using MarkDown which turns into a web page by means of Hyde.
But, meanwhile, some of the projects I am involved in already use some kind of traditional wiki system,
and most of them use <a href="http://canvoki.net/coder/blog/(http://mediawiki.org/)">Mediawiki</a>.</p>
<p>Lucky for me,
this week, I have come across a useful git extension.
It git clones the content of a Mediawiki site as it were a git remote repository
so that you can pull revisions into your hard drive,
edit them and push them back into the wiki.</p>
<h2 id="a-quick-tutorial">A quick tutorial</h2>
<p>You can install it on debian/ubuntu with:</p>
<div class="codehilite"><pre> <span class="n">sudo</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="n">install</span> <span class="n">git</span><span class="o">-</span><span class="n">mediawiki</span>
</pre></div>
<p>Once you do that you can execute:</p>
<div class="codehilite"><pre> <span class="n">git</span> <span class="n">clone</span> <span class="n">mediawiki</span><span class="o">::</span><span class="n">http</span><span class="o">:</span><span class="c1">//clam-project.org/w clam-wiki</span>
</pre></div>
<p>Since 3k4 revisions we have in <span class="caps">CLAM</span> are a quite long download and the wiki api and the
server are quite slow, you can avoid the history with:</p>
<div class="codehilite"><pre> <span class="n">git</span> <span class="n">clone</span> <span class="o">-</span><span class="n">c</span> <span class="n">remote</span><span class="p">.</span><span class="n">origin</span><span class="p">.</span><span class="n">shallow</span><span class="o">=</span><span class="nb">true</span> <span class="n">mediawiki</span><span class="o">::</span><span class="n">http</span><span class="o">:</span><span class="c1">//clam-project.org/w clam-wiki</span>
</pre></div>
<p>Before you push back you must set the wiki user.</p>
<div class="codehilite"><pre> <span class="n">git</span> <span class="n">config</span> <span class="n">remote</span><span class="p">.</span><span class="n">origin</span><span class="p">.</span><span class="n">mwLogin</span> <span class="n">MyUser</span>
</pre></div>
<p>git-mediawiki stores git-commit to mediawiki-revision mappings in a parallel branch.</p>
<h2 id="as-migration-path-way">As migration path way</h2>
<p>This extension is not just useful to edit MediaWiki as it where a git remote repository.</p>
<p>It is a nice path to move your wiki to a different system like Hyde,
by turning the pages into markdown with pandoc.</p>
<div class="codehilite"><pre><span class="k">for </span>mwfile in *.mw
<span class="k">do</span>
<span class="k"> </span>pandoc -f mediawiki -o <span class="k">$(</span>basename <span class="nv">$mwfile</span> .mw<span class="k">)</span>.md <span class="nv">$mwfile</span>
<span class="k">done</span>
</pre></div>
<h2 id="the-wish-list">The wish list</h2>
<p>The tool is quite useful by itself, but there are some edges that could be improved (bug reports linked):</p>
<ul>
<li><a href="https://github.com/moy/Git-Mediawiki/issues/22">Attachments are not included.</a>
So if you have, for instance, images, you won’t have them in local.</li>
<li><strong>Cloning, pulling and pushing are slow.</strong>
Those are the operations that interact with the remote MediaWiki.
All the revision handling intelligence happens at users computer,
so git-mediawiki has to download a lot of information from mediawiki previously to do any action.
MediaWiki <span class="caps">API</span> entry points are not designed with those use cases in mind.</li>
<li><a href="https://github.com/moy/Git-Mediawiki/issues/21">Supages do not generate directories.</a>
For instance, if you have a wiki page named <code>Devel/ToDo</code>, which is a subpage of <code>Devel</code>,
instead of generating a folder <code>Devel</code> and a <code>ToDo.mw</code> file inside,
it replaces the slash by <code>%2F</code>, <code>Devel%2FToDo.mw</code>, which looks quite unreadable when you list the files.</li>
</ul>
<p>It is a pity, that git-mediawiki is written in Perl instead of Python.
If it were written in Python I would be fixing those bugs right now :-)</p>2014-06-14T00:00:00+00:00VokimonHernán Ordiales: AP-Gen new release (LADSPA and VST support)
http://audiores.uint8.com.ar/blog/2013/09/06/ap-gen-new-release-ladspa-and-vst-support/
AP-Gen speeds up and eases the plugin development through base source code generation, both for different standards and operating systems, thus achieving that the developer can focus on his goal, the digital audio processing. To achieve this, starts from normalized … <a href="http://audiores.uint8.com.ar/blog/2013/09/06/ap-gen-new-release-ladspa-and-vst-support/">Continue reading <span class="meta-nav">→</span></a>2013-09-07T06:00:02+00:00Hernán Ordiales: VST cross compiling in Linux
http://audiores.uint8.com.ar/blog/2013/07/29/vst-cross-compiling-in-linux/
1. Install mingw32 and wine: $ sudo apt-get install mingw32 $ sudo apt-get install wine 2. Download Steinberg VST SDK 2.4 and unzip it. 3. Create a PLUGIN_NAME.def file: LIBRARY '' DESCRIPTION '' EXPORTS main=VSTPluginMain 4. … <a href="http://audiores.uint8.com.ar/blog/2013/07/29/vst-cross-compiling-in-linux/">Continue reading <span class="meta-nav">→</span></a>2013-07-30T06:00:04+00:00Hernán Ordiales: Reasons to not use locks: Priority inversion and general purpose vs realtime OS
http://audiores.uint8.com.ar/blog/2013/07/21/reasons-to-not-use-locks-priority-inversion-and-general-purpose-vs-realtime-os/
“Let's say your GUI thread is holding a shared lock when the audio callback runs. In order for your audio callback to return the buffer on time it first needs to wait for your GUI thread to release the lock. … <a href="http://audiores.uint8.com.ar/blog/2013/07/21/reasons-to-not-use-locks-priority-inversion-and-general-purpose-vs-realtime-os/">Continue reading <span class="meta-nav">→</span></a>2013-07-22T06:00:04+00:00David Garcia: The Bla Face
http://canvoki.net/coder/blog/2013-07-09-the-bla-face.html
<div style="display: none;"><img src="http://canvoki.net/coder/media/images/blaface-mainmenu.png" style="float: right;" /></div>
<div></div>
<video controls="controls" style="float: right; display: block; width: 30%; margin: 10pt;">
<source src="/coder/media/blaface-screencast.mp4" />
<source src="/coder/media/blaface-screencast.webm" />
<source src="/coder/media/blaface-screencast.mkv" />
<source src="/coder/media/blaface-screencast.ogv" />
Your browser is not able to display this video.
</video>
<p>My latest experiments involved animated <span class="caps">SVG</span>’s and
webapps for mobile devices (FirefoxOS…).
Also scratches <span class="caps">HTML5</span> <em>audio</em> tag.</p>
<p>The result is this irritating application: <em>The Bla Face</em>.
A talking head that stares around, blinks and speaks the ‘bla’ language.</p>
<p><a href="http://vokimon.github.io/blaface">Take a look at it</a>
and read more if you are interested on how it was done.</p>
<h2 id="animating-inkscape-illustrations">Animating Inkscape illustrations</h2>
<p>I drew the <a href="http://vokimon.github.io/blaface/blaface.svg"><span class="caps">SVG</span> face</a>
as an example for a <a href="http://canvoki.net/coder/blog/TODO">Inkscape course</a>
I was teaching as volunteer in a <a href="http://impulsant.blogspot.com">women association</a>
at <a href="http://en.wikipedia.org/wiki/Sant_Joan_Desp%C3%AD">my town</a>.
This was to show the students,
that, once you have a vectorial drawing,
it is quite easy to animate it like a puppet.
I just moved the parts directly in Inkscape, for example,
moving the nodes of the mouth, or moving the pupils.</p>
<p>Playing with that is quite funny, but the truth is that,
although the <span class="caps">SVG</span> standard provides means to automate animations,
and Internet is full of examples and documentation on how to do it,
it must be done either by changing the <span class="caps">XML</span> (<span class="caps">SMIL</span>, <span class="caps">CSS</span>) or by programming with JavaScript,
there is no <span class="caps">SVG</span> native <span class="caps">FLOSS</span> authoring tool available that I know.
In fact, the state of the art would be something like that:</p>
<ul>
<li><a href="http://www.synfig.org/cms/">Synfig</a>: Full interface to animate, imports and exports svg’s but animation is not native <span class="caps">SVG</span> and <a href="http://wiki.synfig.org/wiki/Sif2svg">you pay the price</a>.</li>
<li><a href="http://www.maefloresta.com/portal/">Tupi</a>: Promising interface concept, working with svg but not at internal level. It still needs work.</li>
<li><a href="http://sozi.baierouge.fr/wiki/en:welcome">Sozi</a> and <a href="http://code.google.com/p/jessyink/">JessyInk</a>: Although they just animate the viewport, not the figures,
and their authoring <span class="caps">UI</span> is quite pedestrian,
I do like how they integrate the animation into the <span class="caps">SVG</span> output.</li>
<li><a href="http://wiki.inkscape.org/wiki/index.php/SpecSVGAnimation">A blue print</a> exists on how to make animations inside Inkscape. Some years ago and still there.</li>
</ul>
<p>So if I want to animate the face I should code some <span class="caps">SMIL</span>/Javascript.
Not something that I could teach my current students,
but, at least, let’s use it as a mean to learn webapp development.
Hands on.</p>
<h2 id="embedding-svg-into-html5-different-ways-unified">Embedding svg into <span class="caps">HTML5</span>, different ways unified.</h2>
<p>The web is full of reference on the different ways to insert an <span class="caps">SVG</span> inside <span class="caps">HTML5</span>.
Just to learn how it works I tried most of them,
I discarded the <em>img</em> method that blocks you the access to the <span class="caps">DOM</span>,
and the <em>embed</em> method which is deprecated.</p>
<h3 id="inline-svg">Inline <span class="caps">SVG</span></h3>
<p>The first method consists on inserting the <span class="caps">SVG</span> inline into the <span class="caps">HTML5</span>,
it has the drawback that every time you edit the <span class="caps">SVG</span> from Inkscape
you have to update the changes.
No problem, there are many techniques to insert it dynamically.
I used an idiom, that I already used for TestFarm for plots, and I like a lot.
That is, a class of <em>div</em> emulating an <em>img</em> with a <em>src</em> attribute.</p>
<div class="codehilite"><pre><span class="c"><!-- Method one: Inline <span class="caps">SVG</span> (dinamically inserted) --></span>
<span class="nt"><div</span>
<span class="na">id=</span><span class="s">'faceit'</span>
<span class="na">class=</span><span class="s">'loadsvg'</span>
<span class="na">src=</span><span class="s">'blaface.svg'</span>
<span class="nt">></div></span>
</pre></div>
<p>Calling the following function (requires <a href="http://jquery.com/">JQuery</a>),
takes all such div tags and uses the <em>src</em> attributes to dynamically load the svg.</p>
<div class="codehilite"><pre><span class="c1">/// For every .loadsvg, loads <span class="caps">SVG</span> file specified by the 'src' attribute</span>
<span class="kd">function</span> <span class="nx">loadsvgs</span><span class="p">()</span>
<span class="p">{</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">each</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s2">".loadsvg"</span><span class="p">),</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">"<span class="caps">GET</span>"</span><span class="p">,</span><span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'src'</span><span class="p">),</span><span class="kc">false</span><span class="p">);</span>
<span class="c1">// Following line is just to be on the safe side;</span>
<span class="c1">// not needed if your server delivers <span class="caps">SVG</span> with correct <span class="caps">MIME</span> type</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">overrideMimeType</span><span class="p">(</span><span class="s2">"image/svg+xml"</span><span class="p">);</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="s2">""</span><span class="p">);</span>
<span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">prepend</span><span class="p">(</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">responseXML</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
</pre></div>
<p>The document to create new elements in this case is the <span class="caps">HTML</span> root, so <em>document</em>
and you can get the root <span class="caps">SVG</span> node by looking up “#faceit > svg”.</p>
<h3 id="object">Object</h3>
<p>The second method is the <em>object</em> tag.</p>
<div class="codehilite"><pre><span class="nt"><object</span>
<span class="na">id=</span><span class="s">'faceit'</span>
<span class="na">data=</span><span class="s">"blaface.svg"</span>
<span class="na">type=</span><span class="s">"image/svg+xml"</span>
<span class="nt">></object></span>
</pre></div>
<p>It is cleaner, since it does not need any additional JavaScript to load.
When using <em>object</em>, the root <span class="caps">SVG</span> element is not even inside the <span class="caps">HTML</span> <span class="caps">DOM</span>.
You have to lookup for the <em>#faceit</em> element and accessing the <em>contentDocument</em> attribute
which is a <span class="caps">DOM</span> document itself.
Because they are different <span class="caps">DOM</span> documents, new <span class="caps">SVG</span> elements
can not be created, as we did previously, from the <span class="caps">HTML</span> document.</p>
<p>This couple of functions will abstract this complexity from the rest of the code:</p>
<div class="codehilite"><pre><span class="kd">function</span> <span class="nx">svgRoot</span><span class="p">()</span>
<span class="p">{</span>
<span class="kd">var</span> <span class="nx">container</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="s2">"#faceit"</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
<span class="c1">// For object and embed</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">container</span><span class="p">.</span><span class="nx">contentDocument</span><span class="p">)</span>
<span class="k">return</span> <span class="nx">container</span><span class="p">.</span><span class="nx">contentDocument</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">$</span><span class="p">(</span><span class="nx">container</span><span class="p">).</span><span class="nx">children</span><span class="p">();</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">svgNew</span><span class="p">(</span><span class="nx">elementType</span><span class="p">)</span>
<span class="p">{</span>
<span class="nx">svg</span> <span class="o">=</span> <span class="nx">svgRoot</span><span class="p">();</span>
<span class="k">try</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">svg</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="nx">svgns</span><span class="p">,</span> <span class="nx">elementType</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">catch</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// When svg is inline, no svg document, use the html document</span>
<span class="k">return</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="nx">svgns</span><span class="p">,</span> <span class="nx">elementType</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<h3 id="iframe">iframe</h3>
<p>I don’t like that much the <em>iframe</em> solution,
because instead of adapting automatically to the size of the image,
you have to set it by hand, clipping the image if you set it wrong.
But it works in older browsers and it is not deprecated like <em>embed</em>:</p>
<div class="codehilite"><pre><span class="nt"><iframe</span>
<span class="na">id=</span><span class="s">'faceit'</span>
<span class="na">src=</span><span class="s">"blaface.svg"</span>
<span class="na">type=</span><span class="s">"image/svg+xml"</span>
<span class="na">height=</span><span class="s">'350px'</span>
<span class="na">width=</span><span class="s">'250px'</span>
<span class="na">style=</span><span class="s">'border: none; text-align:center;'</span>
<span class="nt">></iframe></span>
</pre></div>
<p>You can also play with the <span class="caps">SVG</span> view port to get the <span class="caps">SVG</span> resized,
without losing proportions.</p>
<p>In terms of JavaScript, the same code that works for <em>object</em> works for <em>iframe</em>.</p>
<h3 id="css">css</h3>
<p>The <span class="caps">CSS</span> part of the head so that whatever the method they look the same.</p>
<h2 id="animating-the-eye-pupils">Animating the eye pupils</h2>
<p>Before doing any animation, my advice:
change the automatic ids of the <span class="caps">SVG</span> objects to be animated
into something nice.
You can use object properties dialog or the <span class="caps">XML</span> view in Inkscape.</p>
<p>Eye pupils can be moved to stare around randomly.
Both pupils have been grouped so that moving such group, <em>#eyepupils</em>, is enough.
The JavaScript code that moves it follows:</p>
<div class="codehilite"><pre><span class="kd">var</span> <span class="nx">previousGlance</span> <span class="o">=</span> <span class="s1">'0,0'</span>
<span class="kd">function</span> <span class="nx">glance</span><span class="p">()</span>
<span class="p">{</span>
<span class="kd">var</span> <span class="nx">svg</span> <span class="o">=</span> <span class="nx">svgRoot</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">eyes</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">svg</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="s2">"#eyepupils"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">eyesanimation</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">eyes</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="s2">"#eyesanimation"</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">eyesanimation</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span>
<span class="p">{</span>
<span class="nx">eyesanimation</span> <span class="o">=</span> <span class="nx">svgNew</span><span class="p">(</span><span class="s2">"animateMotion"</span><span class="p">);</span>
<span class="nx">$</span><span class="p">(</span><span class="nx">eyesanimation</span><span class="p">).</span><span class="nx">attr</span><span class="p">({</span>
<span class="s1">'id'</span><span class="o">:</span> <span class="s1">'eyesanimation'</span><span class="p">,</span>
<span class="s1">'begin'</span><span class="o">:</span> <span class="s1">'indefinite'</span><span class="p">,</span> <span class="c1">// Required to trigger it at will</span>
<span class="s1">'dur'</span><span class="o">:</span> <span class="s1">'0.3s'</span><span class="p">,</span>
<span class="s1">'fill'</span><span class="o">:</span> <span class="s1">'freeze'</span><span class="p">,</span>
<span class="p">});</span>
<span class="nx">$</span><span class="p">(</span><span class="nx">eyes</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">eyesanimation</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">x</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="mi">15</span><span class="o">-</span><span class="mi">7</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="mi">10</span><span class="o">-</span><span class="mi">5</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">currentGlance</span> <span class="o">=</span> <span class="p">[</span><span class="nx">x</span><span class="p">,</span><span class="nx">y</span><span class="p">].</span><span class="nx">join</span><span class="p">(</span><span class="s1">','</span><span class="p">);</span>
<span class="nx">$</span><span class="p">(</span><span class="nx">eyesanimation</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'path'</span><span class="p">,</span> <span class="s2">"M "</span><span class="o">+</span><span class="nx">previousGlance</span><span class="o">+</span><span class="s2">" L "</span><span class="o">+</span><span class="nx">currentGlance</span><span class="p">);</span>
<span class="nx">previousGlance</span> <span class="o">=</span> <span class="nx">currentGlance</span><span class="p">;</span>
<span class="nx">eyesanimation</span><span class="p">.</span><span class="nx">beginElement</span><span class="p">();</span>
<span class="nx">nextGlance</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="mi">1000</span><span class="o">+</span><span class="mi">4000</span><span class="p">;</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">glance</span><span class="p">,</span> <span class="nx">nextGlance</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">glance</span><span class="p">();</span>
</pre></div>
<p>So the strategy is introducing an <em>animateMotion</em> element into the group,
or reusing the previous one,
set the motion, trigger the annimation and reprogram the next glance.</p>
<h2 id="animating-mouth-and-eyelids">Animating mouth and eyelids</h2>
<p><img src="http://canvoki.net/coder/media/images/blaface-eyelidscontrolpoints.png" style="float: right; padding: 10px;" /></p>
<p>To animate eyelids and mouth,
instead of moving an object we have to move control nodes of a path.
Control nodes are not first class citizens in <span class="caps">SVG</span>,
they are <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths">encoded using a compact format</a>
as the string value of the <em>d</em> attribute of the path.
I added the following function to convert structured <span class="caps">JS</span> data into such string:</p>
<div class="codehilite"><pre><span class="kd">function</span> <span class="nx">encodePath</span><span class="p">(</span><span class="nx">path</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nx">path</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">$</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">e</span><span class="p">))</span> <span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s2">","</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">e</span><span class="p">;</span>
<span class="p">}).</span><span class="nx">join</span><span class="p">(</span><span class="s2">" "</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>With this helper, simpler functions to get parametrized variations on a given object
become more handy.
For instance, to have a mouth path with parametrized opening factor:</p>
<div class="codehilite"><pre><span class="kd">function</span> <span class="nx">mouthPath</span><span class="p">(</span><span class="nx">openness</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nx">encodePath</span><span class="p">([</span>
<span class="s2">"M"</span><span class="p">,</span>
<span class="p">[</span><span class="mf">173.28125</span><span class="p">,</span> <span class="mf">249.5</span><span class="p">],</span>
<span class="s2">"L"</span><span class="p">,</span>
<span class="p">[</span><span class="mf">71.5625</span><span class="p">,</span> <span class="mf">250.8125</span><span class="p">],</span>
<span class="s2">"C"</span><span class="p">,</span>
<span class="p">[</span><span class="mf">81.799543</span><span class="p">,</span> <span class="mf">251.14273</span><span class="p">],</span>
<span class="p">[</span><span class="mf">103.83158</span><span class="p">,</span> <span class="mf">253.0</span><span class="o">+</span><span class="nx">openness</span><span class="p">],</span> <span class="c1">// Incoming tangent</span>
<span class="p">[</span><span class="mf">121.25</span><span class="p">,</span> <span class="mf">253.0</span><span class="o">+</span><span class="nx">openness</span><span class="p">],</span> <span class="c1">// Mid lower point</span>
<span class="s2">"C"</span><span class="p">,</span>
<span class="p">[</span><span class="mf">138.66843</span><span class="p">,</span> <span class="mf">253.0</span><span class="o">+</span><span class="nx">openness</span><span class="p">],</span> <span class="c1">// Outgoing tangent</span>
<span class="p">[</span><span class="mf">160.7326</span><span class="p">,</span> <span class="mf">251.48139</span><span class="p">],</span>
<span class="p">[</span><span class="mf">173.28125</span><span class="p">,</span> <span class="mf">249.5</span><span class="p">],</span>
<span class="s2">"z"</span>
<span class="p">]);</span>
<span class="p">}</span>
</pre></div>
<p>And to apply it:</p>
<div class="codehilite"><pre><span class="nx">$</span><span class="p">(</span><span class="nx">svgRoot</span><span class="p">()).</span><span class="nx">find</span><span class="p">(</span><span class="s2">"#mouth"</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">"d"</span><span class="p">,</span> <span class="nx">mouthPath</span><span class="p">(</span><span class="mi">20</span><span class="p">));</span>
</pre></div>
<p>But if we want a soft animation we should insert an attribute animation.
For example if we want to softly open and close the mouth like saying ‘bla’
the function wouldbe quite similar to the one for the eye pupils,
but now we use an <em>animate</em> instead <em>animateMotion</em>
and specify the <em>attributeName</em> instead <em>mpath</em>,
and instead of providing the movement path, we provide a sequence of paths
to morph along them separated by semicolons.</p>
<div class="codehilite"><pre><span class="kd">function</span> <span class="nx">bla</span><span class="p">()</span>
<span class="p">{</span>
<span class="kd">var</span> <span class="nx">svg</span> <span class="o">=</span> <span class="nx">svgRoot</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">mouth</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">svg</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="s2">"#mouth"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">blaanimation</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">mouth</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="s2">"#blaanimation"</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">blaanimation</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span>
<span class="p">{</span>
<span class="nx">blaanimation</span> <span class="o">=</span> <span class="nx">svgNew</span><span class="p">(</span><span class="s2">"animate"</span><span class="p">);</span>
<span class="nx">$</span><span class="p">(</span><span class="nx">blaanimation</span><span class="p">).</span><span class="nx">attr</span><span class="p">({</span>
<span class="s1">'attributeName'</span><span class="o">:</span> <span class="s1">'d'</span><span class="p">,</span>
<span class="s1">'id'</span><span class="o">:</span> <span class="s1">'blaanimation'</span><span class="p">,</span>
<span class="s1">'begin'</span><span class="o">:</span> <span class="s1">'indefinite'</span><span class="p">,</span>
<span class="s1">'dur'</span><span class="o">:</span> <span class="mf">0.3</span><span class="p">,</span>
<span class="p">});</span>
<span class="nx">$</span><span class="p">(</span><span class="nx">mouth</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">blaanimation</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">syllable</span> <span class="o">=</span> <span class="p">[</span>
<span class="nx">mouthPath</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span>
<span class="nx">mouthPath</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span>
<span class="nx">mouthPath</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span>
<span class="p">].</span><span class="nx">join</span><span class="p">(</span><span class="s2">";"</span><span class="p">);</span>
<span class="nx">$</span><span class="p">(</span><span class="nx">blaanimation</span><span class="p">)</span>
<span class="p">.</span><span class="nx">off</span><span class="p">()</span>
<span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'values'</span><span class="p">,</span> <span class="nx">syllable</span><span class="p">)</span>
<span class="p">;</span>
<span class="nx">blaanimation</span><span class="p">.</span><span class="nx">beginElement</span><span class="p">();</span>
<span class="nx">sayBla</span><span class="p">();</span> <span class="c1">// Triggers the audio</span>
<span class="nx">nextBla</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="mi">2000</span><span class="o">+</span><span class="mi">600</span><span class="p">;</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">bla</span><span class="p">,</span> <span class="nx">nextBla</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>The actual code is quite more complicated because it makes words of many syllables (bla’s)
and tries to synchronize the lipsing with audio.
First of all, using the <em>repeatCount</em> attribute to be a random number between 1 and 4.</p>
<div class="codehilite"><pre> <span class="kd">var</span> <span class="nx">syllables</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="mi">4</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span>
<span class="nx">$</span><span class="p">(</span><span class="nx">blaanimation</span><span class="p">)</span>
<span class="p">.</span><span class="nx">off</span><span class="p">()</span>
<span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'values'</span><span class="p">,</span> <span class="nx">syllable</span><span class="p">)</span>
<span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'repeatCount'</span><span class="p">,</span> <span class="nx">syllables</span><span class="p">)</span>
<span class="p">;</span>
</pre></div>
<p>And then spacing them proportional to the word length:</p>
<div class="codehilite"><pre> <span class="kd">var</span> <span class="nx">wordseconds</span> <span class="o">=</span> <span class="p">(</span><span class="nx">syllables</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="mf">0.3</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">nextBla</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="mi">2000</span><span class="o">+</span><span class="nx">wordseconds</span><span class="o">*</span><span class="mi">1000</span><span class="p">;</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">bla</span><span class="p">,</span> <span class="nx">nextBla</span><span class="p">);</span>
</pre></div>
<p>Regarding the lipsing, *sayBla is defined like:</p>
<div class="codehilite"><pre><span class="kd">function</span> <span class="nx">sayBla</span><span class="p">()</span>
<span class="p">{</span>
<span class="nx">blaaudio</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">"#blaaudio"</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
<span class="nx">blaaudio</span><span class="p">.</span><span class="nx">pause</span><span class="p">();</span>
<span class="nx">blaaudio</span><span class="p">.</span><span class="nx">currentTime</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
<span class="nx">blaaudio</span><span class="p">.</span><span class="nx">play</span><span class="p">();</span>
<span class="p">}</span>
</pre></div>
<p>So the smart move is adding a handler to the <em>repeat</em> event of the animation.
But this seems not to work on Chrome.
Instead we draw on a timer again.</p>
<div class="codehilite"><pre> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="nx">i</span><span class="o"><</span><span class="nx">syllables</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">sayBla</span><span class="p">,</span> <span class="nx">i</span><span class="o">*</span><span class="mf">0.3</span><span class="o">*</span><span class="mi">1000</span><span class="p">);</span>
</pre></div>
<p>When animating the eyelids, more browser issues pop up.
The eyelid on one eye is an inverted and displaced clone of the other.
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=738574">Firefox won’t apply to clones javascript triggered animations</a>.
If you set the values without animation, they work,
if they are triggered by the begin attribute, they work,
but if you trigger an animation with <em>beginElement</em>, it won’t work.</p>
<h2 id="user-interface-and-firefoxos-integration">User interface and FirefoxOS integration</h2>
<p><img src="http://canvoki.net/coder/media/images/blaface-settings.png" style="height: 400px; float: right; margin: 0px;" /></p>
<p>Flashy buttons and checkboxes,
panel dialogs that get hidden,
the debug log side panel…
All that is CSSery i tried to make simple enough so that it can be pulled out.
So just take a look at the <span class="caps">CSS</span>.</p>
<p>As I said, besides <span class="caps">SVG</span> animation I wanted to learn webapp development for FirefoxOS.
My first glance at the environment as developer has been a mix of good and bad impressions.
On one side, using Linux + Gecko as the engine for the whole system is quite smart.
The simulator is clearly an alpha that eats many computer resources.
Anyway let’s see how it evolves.</p>
<p>This project I tried to minimized the use of libraries,
just using [requirejs] (a library dependency solver)
and [Zepto] (a reduced <a href="http://jquery.com/">JQuery</a>) because the
minimal Firefox example already provides them.
But there are a wide ecology of them everybody uses
Next thing to investigate is how to work with <a href="http://volojs.org/">VoloJs</a> on how to deploy projects,
and that wide ecology of libraries available.</p>
<p>You have many foundation <a href="http://jquery.com/">JQuery</a> like frameworks such as
<a href="http://prototypejs.org/">Prototype</a>, <a href="http://underscorejs.org/">Underscore</a>, <a href="http://backbonejs.org/">Backbone</a>…
Then you have libraries for user interface components such as:
<a href="http://dojotoolkit.org/">Dojo</a>, <a href="http://canvoki.net/coder/blog/atom.xml">JQuery Mobile</a>, <a href="http://facebook.github.io/react/">React</a>, <a href="http://yuilibrary.com/yui/quick-start/"><span class="caps">YUI</span></a>, <a href="http://eightmedia.github.io/hammer.js/">Hammer</a>, <a href="http://w2ui.com/web/">w2ui</a>, <a href="http://www.the-m-project.org/">m-project</a>…
Too many to know which is the one to use.</p>2013-07-09T00:00:00+00:00VokimonCLAM News: TestFarm 2.0 released
https://clamnews.wordpress.com/2013/05/07/testfarm-2-0-released/
<p>We just released TestFarm 2.0. Now on <a href="https://github.com/clam-project/testfarm">GitHub</a>.</p>
<p>You can install it by running:</p>
<pre>sudo pip install testfarm
</pre>
<p>In Debian/Ubuntu, if you installed python-stdeb first, it will be installed as a deb package you can remove as other debian packages.</p>
<p>This release is a major rewrite on the server side. You can expect it more reliable, more scalable and easier to install. It is also easier to maintain.<br />
Most changes are at the server and the client-server interface. Client API is mostly the same and migration of existing clients should be quite straight forward.</p>
<p>Regarding CLAM, it would be nice if we can get <a href="http://clam-project.org/testfarm.html">a bunch of CLAM testfarm clients</a>. Now clients are easier to setup. In order to setup one, please, contact us.</p>2013-05-07T12:27:16+00:00vokimonCLAM News: CLAM at Debian!
https://clamnews.wordpress.com/2011/04/04/clam-at-debian/
<p>CLAM finally made its way into the official Debian repositories. Many thanks to the maintainer, Taniguchi Takaki.</p>
<p><a href="http://packages.debian.org/source/sid/clam">http://packages.debian.org/source/sid/clam</a><br />
<a href="http://packages.debian.org/source/sid/clam-networkeditor">http://packages.debian.org/source/sid/clam-networkeditor</a><br />
<a href="http://packages.debian.org/source/sid/clam-chordata">http://packages.debian.org/source/sid/clam-chordata</a></p>2011-04-04T23:13:20+00:00vokimonCLAM News: Ubuntu PPA for CLAM
https://clamnews.wordpress.com/2011/04/04/ubuntu-ppas-for-clam/
<p>For the convenience of Ubuntu users, we deployed a personal package archive (PPA) in launchpad.</p>
<p><a href="https://launchpad.net/%7Edgarcia-ubuntu/+archive/ppa" rel="nofollow">https://launchpad.net/~dgarcia-ubuntu/+archive/ppa</a></p>
<p>Instructions available at the same page. It currently contains libraries, extension plugins, NetworkEditor and Chordata packages for maverick, and platforms i386 and amd64.</p>2011-04-04T23:03:43+00:00vokimonHernán Ordiales: High abstraction level audio plugins specification (and code generation)
http://audiores.uint8.com.ar/blog/2010/05/17/high-abstraction-level-audio-plugins-specification-and-code-generation/
If you ever wrote at least 2 audio plugins in your life, for sure you have noticed you had to write a lot of duplicated code. In other words, most of the times, writing a plugin there is very little … <a href="http://audiores.uint8.com.ar/blog/2010/05/17/high-abstraction-level-audio-plugins-specification-and-code-generation/">Continue reading <span class="meta-nav">→</span></a>2010-09-20T00:35:52+00:00CLAM News: CLAM Chordata 1.0
https://clamnews.wordpress.com/2010/03/08/clam-chordata-1-0/
<div style="float: right; margin: 3px;"><a href="http://clam-project.org/w/images/6/6c/TurnAround-colorSegments.png"><img alt="screenshot" src="https://i2.wp.com/clam-project.org/w/images/6/6c/TurnAround-colorSegments.png" width="220px" /></a></div>
<p>The CLAM project is pleased to announce the first stable release of Chordata, which is released in parallel to the 1.4.0 release of the CLAM framework.</p>
<p>Chordata is a simple but powerful application that analyses the chords of any music file in your computer. You can use it to travel back and forward the song while watching insightful visualizations of the tonal features of the song. Key bindings and mouse interactions for song navigation are designed thinking in a musician with an instrument at hands.</p>
<p>Chordata in live: <a href="http://www.youtube.com/watch?v=xVmkIznjUPE">http://www.youtube.com/watch?v=xVmkIznjUPE</a><br />
The tutorial: <a href="http://clam-project.org/wiki/Chordata_tutorial">http://clam-project.org/wiki/Chordata_tutorial</a><br />
Downloat it at <a href="http://clam-project.org">http://clam-project.org</a></p>
<p>This application was developed by Pawel Bartkiewicz as his GSoC 2008 project, by using existing CLAM technologies under a more suited interface which is now Chordata. Please, enjoy it.</p>2010-03-08T21:13:09+00:00vokimonGreg Kellum: wired magazine
http://gregkellum.com/blog///index.php/2009/04/03/wired-magazine?blog=5
<p>I was in Wired magazine last week: </p>
<p><a href="http://www.wired.com/gadgets/mods/multimedia/2009/03/gallery_instruments?slide=22&slideView=3">http://www.wired.com/gadgets/mods/multimedia/2009/03/gallery_instruments?slide=22&slideView=3</a></p><div class="item_footer"><p><small><a href="http://gregkellum.com/blog///index.php/2009/04/03/wired-magazine?blog=5">Original post</a> blogged on <a href="http://b2evolution.net/">b2evolution</a>.</small></p></div>2009-04-03T09:04:53+00:00symfonysidRoman Goj: Chord Segmentation: first results are here!
http://ro-baczek.blogspot.com/2007/08/chord-segmentation-first-results-are.html
Hullo Planet!<br />Three months after starting this blog, finally the first post...<br />...because finally I have something nice to show off.<br /><br />My <a href="http://code.google.com/soc/">Google Summer Of Code</a> task is enhancing realtime chord extraction in <a href="http://clam.iua.upf.edu/">CLAM</a>. So far I've been working on small changes, refactorings, etc. But now I took a small break from that to check whether I can really improve the chord segmentation.<br /><br />The chord extraction algorithm in CLAM is really good but has a very "raw" output - not exactly something one could use to learn the chords of a favourite song. A big part of my GSoC task was changing this. And the first results are here:<br /><a href="http://2.bp.blogspot.com/_7Q2rGqIQOAk/RrxP3IVnn3I/AAAAAAAAAAw/fLsmwKNwapg/s1600-h/screenshot.png"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5097036686826119026" src="http://2.bp.blogspot.com/_7Q2rGqIQOAk/RrxP3IVnn3I/AAAAAAAAAAw/fLsmwKNwapg/s400/screenshot.png" style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 392px; height: 167px;" /></a><br />The screenshot shows ChordExtractor output as viewed with the Annotator. The song being analysed is Debaser-WoodenHouse.mp3. The upper half of the screenshot shows the old output (to be exact the ChordExtractor from current svn, as extracted with my computer using fftw3). The lower half shows the new improved segmentation (notice the chord segments are much bigger, not that, well - segmented).<br /><br />Problem is - this code exists only in my sandbox for now... I unfortunately reverted to my old pre-svn methods of programming - more or less just jabbing at the code as long as the number of segfaults stays manageable (just one with this code, shows how simple the changes are!). The next few days will hopefully see it cleaned and committed to the svn.<br /><br />What this new improved segmentation actually does ...<br /><br />Some chords are very similar to others i.e. C# Minor differs from A Major by just one note (G# exchanged for A). When you play just the two common notes for the first 5 seconds and then a full chord for the next 5, you'll know that you're not really changing the chords... but the old algorithm would probably show you a mix of both chords during the first 5 seconds.<br /><br />The new algorithms calulates a chord similarity matrix and takes this similarity into account when deciding whether a new segment really needs to be inserted. This is enough to produce the results above. I still hope this simplicity will allow some nice improvements... but this is still to be seen (hopefully before the GSoC deadline, *gulp*).<br /><br />For anyone wishing to see the results, links to the new and old ChordExtractor .pool files for the songs that come as examples with Annotator:<br /><a href="http://www.box.net/shared/ihh2gdfono">Debaser, Wooden House, old</a><br /><a href="http://www.box.net/shared/vmvqdcy15t">Debaser, Wooden House, new</a><br /><a href="http://www.box.net/shared/vmvqdcy15t">Debaser, Coffee Smell, old</a><br /><a href="http://www.box.net/shared/vmvqdcy15t">Debaser, Coffee Smell, new</a>2008-12-10T22:19:41+00:00RomanGreg Kellum: see you in the half light II
http://gregkellum.com/blog///index.php/2008/10/01/see-you-in-the-half-light-ii?blog=5
<p>Things seem to be moving forward with the music I'm working on at the moment.</p>
<p><a href="http://www.gregkellum.com/media/see_you_in_the_halflight_p1-2.mp3">http://www.gregkellum.com/media/see_you_in_the_halflight_p1-2.mp3</a></p>
<p>I had been trying to figure out how to create a low bass drone a bit like the one in NIN's "Something I Can Never Have". I was playing with some samples of rubbed glass and found that you get something really nice if you passed them through a waveshaper and then a low pass filter...</p>
<p>I was also experimenting with different ways to manipulate bell samples and what's worked so far for this piece has been using the spectral delay from RTCmix, the GrainStream effect from Hipno, and morphing bells together with the babbling of overlaid voices...</p><div class="item_footer"><p><small><a href="http://gregkellum.com/blog///index.php/2008/10/01/see-you-in-the-half-light-ii?blog=5">Original post</a> blogged on <a href="http://b2evolution.net/">b2evolution</a>.</small></p></div>2008-10-01T16:16:30+00:00symfonysidGreg Kellum: i'll see you in the half light...
http://gregkellum.com/blog///index.php/2008/08/22/i-ll-see-you-in-the-half-light?blog=5
<p>I've been working lately on a piece about a girl who I had recurring dreams about. The dreams went on for quite a while, and sometimes, they were blissful and at othertimes upsetting. I've been trying to capture all of the different moods of these dreams in one piece, and at the moment I'm working on a part that's supposed to sound reverential. Here's a piece of it:</p>
<p><a href="http://www.gregkellum.com/media/half_light_v4_bit.mp3">http://www.gregkellum.com/media/half_light_v4_bit.mp3</a></p>
<p>There was a passage in Rilke's Elegies where he writes something along the lines of "beauty is a terror which we can still sustain because it disdains to destroy us." I've been trying to capture the mood of terror inspiring beauty in this piece. I've been concentrating on the memory of the feeling of my heart when it's beating violently in my chest and finding the music which flows naturally from this state. But it's been difficult to conjure up this feeling and the corresponding music, and everything I've done so far hasn't sounded particularly like these vague musical thoughts which come and go...</p><div class="item_footer"><p><small><a href="http://gregkellum.com/blog///index.php/2008/08/22/i-ll-see-you-in-the-half-light?blog=5">Original post</a> blogged on <a href="http://b2evolution.net/">b2evolution</a>.</small></p></div>2008-08-22T14:27:02+00:00symfonysid