559 lines
No EOL
53 KiB
XML
559 lines
No EOL
53 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Caught in the Net (Posts about programming)</title><link>francescomecca.eu</link><description></description><atom:link href="francescomecca.eu/categories/programming.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2020 <a href="mailto:francescomecca.eu">Francesco Mecca</a> </copyright><lastBuildDate>Wed, 29 Jan 2020 10:04:35 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Un articolo per r/italyinformatica</title><link>francescomecca.eu/blog/2018/07/27/addio-reddit/</link><dc:creator>Francesco Mecca</dc:creator><description><div><p>Questo articolo è stato originalmente scritto per il <a href="https://tldr.italyinformatica.org">blog</a> di <a href="https://reddit.com/r/italyinformatica">r/italyinformatica</a>.</p>
|
|
<p>Negli ultimi anni abbiamo assistito all'ascesa di un gran numero di linguaggi di programmazione, in particolare <a href="https://golang.rg">Go</a> (2009), <a href="https://www.rust-lang.org/en-US/">Rust</a> (2010), <a href="https://kotlinlang.org/">Kotlin</a> (2011), <a href="https://elixir-lang.org/">Elixir</a> (2011), <a href="https://crystal-lang.org/">Crystal</a> (2014), <a href="https://www.ponylang.or">Pony</a> (2014).</p>
|
|
<p>Questa esplosione di nuovi linguaggi è dovuta, fra le molte motivazioni, alla necessità di adottare paradigmi di programmazione non immediatamente recenti come cittadini di primo tipo.</p>
|
|
<p>Rispetto ai più maturi C, C++ o Java, Python o Ruby questi linguaggi offrono "out of the box" supporto per:</p>
|
|
<ul>
|
|
<li>una visione moderna delle concorrenze (le goroutines di Go o il modello ad attori di Pony ed Elixir)</li>
|
|
<li>Memory safeness, in particolare:<ul>
|
|
<li>assenza di NULL (Pony, Rust, Kotlin)</li>
|
|
<li>gestione automatica della memoria, il cosiddetto <a href="https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)%20(o%20%5BReference%20Counting%5D(https://en.wikipedia.org/wiki/Reference_counting)%20per%20Rust">Garbage Collector</a></li>
|
|
<li>assenza di puntatori</li>
|
|
<li>assenza di deadlocks</li>
|
|
</ul>
|
|
</li>
|
|
<li>Supporto ad HTTP nella standard library</li>
|
|
<li>Management delle dipendenze (ad eccezione di Kotlin)</li>
|
|
<li>Namespaces</li>
|
|
</ul>
|
|
<p>Chiaramente nessuno di questi linguaggi è oggettivamente superiore agli altri, sono tutti <a href="https://en.wikipedia.org/wiki/Turing_completeness">turing completi</a> e la scelta del programmatore ricade su motivazioni del tutto personali (stile, programmazione ad oggetti, familiarità con altri linguaggi).</p>
|
|
<h3>Un pò di contesto</h3>
|
|
<p>Ho scritto la mie prime due righe di codice nel 2013 per il corso di Computer Science del Politecnico di Torino.</p>
|
|
<p>Per mia fortuna, al Politecnico le cose si muovono ancora lentamente e ci hanno fatto usare C per tutta la durata della triennale. Si è aggiunto un corso di principi della programmazione ad oggetti al terzo anno, in Java.</p>
|
|
<p>Personalmente ho imparato durante l'estate di quel primo anno le basi di C++ e Python poco dopo.</p>
|
|
<p>Sono stati con i miei primi <a href="https://github.com/framecca">progetti</a> che ho capito che non si può avere un buon linguaggio senza un buon tooling ed una community vivace e soprattutto tanta, tanta documentazione. Per questo, per molto tempo C è stata la mia prima scelta, dato che il mio target è sempre Linux.</p>
|
|
<h3>Gli obbiettivi prima del linguaggio</h3>
|
|
<p>Questo post vuole essere una raccolta più o meno organizzata delle motivazioni per cui mi sono dovuto muovere oltre la frontiera di C nel mio ultimo progetto, ovvero un backend per la raccolta e presentazione di pubblicazione di ricerca e materiale didattico per più di 80 centri di ricerca.</p>
|
|
<p>Per il Centro <a href="http://nexa.polito.it/">Nexa</a> del Politecnico di Torino mi sono ritrovato per la prima volta responsabile del codice che dovevo scrivere e dell'uso che se ne sarebbe fatto.</p>
|
|
<p>Ho dovuto tenere in considerazione, oltre chiaramente alla funzionalità della piattaforma, in ordine di priorità:</p>
|
|
<ol>
|
|
<li>Sicurezza</li>
|
|
<li>Performance e scalabilità</li>
|
|
<li>Separazione dal frontend</li>
|
|
<li>Facilità di deploy in un server che non controllo</li>
|
|
</ol>
|
|
<p>Fortunatamente non mi è stata imposta nessuna limitazione sulla scelta del linguaggio, altrimenti Python sarebbe stato la scelta più adeguata se avessi dovuto tener conto anche di altri programmatori.</p>
|
|
<h3>Benvenuto D</h3>
|
|
<p>La mia scelta è caduta su <a href="https://dlang.org">D</a>.</p>
|
|
<p>Voglio provare ad affrontare ad uno ad uno i motivi di questa scelta magari inusuale.</p>
|
|
<h5>Sicurezza</h5>
|
|
<p>Nessuno vuole davvero vantarsi di usare un backend scritto in C/C++. Il <a href="https://en.wikipedia.org/wiki/Buffer_overflow">buffer overflow</a> può essere considerato il bug più comune e ci sono <a href="https://techaeris.com/2017/09/27/buzz-fuzzing-find-uncommon-vulnerability/">situazioni</a> in cui non appaiono affatto in maniera ovvia.</p>
|
|
<p>Inoltre per un applicativo distribuito il Garbage Collector è la scelta più performante, <a href="https://www.usenix.org/node/189882">specialmente se coordinato fra le varie istanze</a>.</p>
|
|
<p>D offre questo di design, e benchè il suo GC sia frutto di numerose <a href="https://www.quora.com/Which-language-has-the-brightest-future-in-replacement-of-C-between-D-Go-and-Rust-And-Why/answer/Andrei-Alexandrescu">discussioni</a>, offre in maniera del tutto innovativa, robustezza e <a href="http://www.walterbright.com/gonewild.pdf">sicurezza</a>.</p>
|
|
<p>In particolare, D presenta:</p>
|
|
<ul>
|
|
<li>Array che sono slices (o ranges) ma non puntatori (e neanche oggetti)</li>
|
|
<li>Bound checking durante la fase di compilazione.</li>
|
|
<li>Inizializzazione automatica delle variabili.</li>
|
|
<li>Safe Casting (chiaramente come eredità di C++).</li>
|
|
<li>Restricted pointers: si può passare una funzione per referenza dichiarandola <code>ref</code>, ma solo quando passata come parametro o di ritorno. Inoltre non c'è nessuna pointer arithmetic.</li>
|
|
<li><a href="https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization">RAII</a>, ovvero l'acquisizione delle risorse equivale alla loro assegnazione e Scopes: le variabili hanno una lifetime limitata allo scope di dichiarazione. Nessun dangling pointer come in C.</li>
|
|
<li>Strutture immutabili: come nelle specifiche di molti linguaggi funzionali, si può dichiarare una variabile come <code>immutable</code> e quindi può essere facilmente condivisa fra threads.</li>
|
|
<li>@safe, @trusted: le specifiche del linguaggio permettono di annotare delle funzioni come sicure o affidabili affinchè il compilatore controlli che non gestiscano puntatori (ad esempio interfacciandosi con C) ed utilizzino il subset "sicuro" del linguaggio (maggiori dettagli in seguito).</li>
|
|
<li>funzioni pure: le funzioni inoltre possono essere dichiarate pure, prive di effetti collaterali e sempre <a href="https://en.wikipedia.org/wiki/Reentrancy_(computing)">rientranti</a>. Questo permette di evitare <a href="https://en.wikipedia.org/wiki/Deadlock">deadlocks</a> e un controllo totale sul risultato delle funzioni.</li>
|
|
</ul>
|
|
<h5>Performance</h5>
|
|
<p>Ci sono moltissime soluzioni per scrivere un applicativo che si interfaccia con il web, ma hanno tutte la loro origine nel famoso <a href="http://www.kegel.com/c10k.html">C10K problem</a>.</p>
|
|
<p>Nel mio caso ho deciso di utilizzare un approccio <a href="https://stackoverflow.com/questions/10960998/how-different-async-programming-is-from-threads">asincrono</a> con coroutines (anche detti <a href="https://en.wikipedia.org/wiki/Green_threads">threads leggeri</a>).</p>
|
|
<p>Benchè D abbia <a href="https://dlang.org/library/core/thread/fiber.html">supporto nativo</a> alle coroutines, ho deciso di appoggiarmi al framework più comune per web dev in D: <a href="francescomecca.eu/blog/2018/07/27/addio-reddit/vibed.org">vibe.d</a>.</p>
|
|
<p>Ogni volta che Vibe accetta una richiesta dall'esterno ed esegue una funzione bloccante (che interrompe l'esecuzione del programma fino al ritorno della funzione), questa viene messa in una pool di azioni da eseguire e Vibe controlla periodicamente che almeno una di queste sia pronta a ritornare un risultato e continuare con l'esecuzione di questa.</p>
|
|
<p>Inoltre, benchè questo meccanismo funzioni interamente su un solo thread, è elementare coordinare una <a href="https://en.wikipedia.org/wiki/Thread_pool">thread pool</a> che distribuisa il carico fra i vari core che eseguono migliaia di threads leggeri concorrentemente.</p>
|
|
<h5>Contratti e Tests</h5>
|
|
<p>Non amo scrivere commenti sui programmi. Penso sia assolutamente necessario commentare il codice di librerie ma al di fuori di queste il codice (buon codice) dovrebbe essere autoesplicativo.</p>
|
|
<p>Inoltre, nelle mie recenti esperienze, il comportamento del programma era chiaro a partire dai tests.</p>
|
|
<p>In D questo concetto viene portato agli estremi applicando il <a href="https://en.wikipedia.org/wiki/Design_by_contract">"Design by Contract programming</a>.</p>
|
|
<p>Un contratto è la divisione di una funzione in:</p>
|
|
<ul>
|
|
<li>Precondizione, ovvero le condizioni che devono essersi verificate prima della chiamata della funzione;</li>
|
|
<li>Postcondizione, ovvero le condizioni che devono essere rispettate all'uscita della funzione (solitamente applicate al risultato);</li>
|
|
<li>Invarianti, ovvero le specifiche di una struttura dati che devono rimanere verificate in ogni funzione;</li>
|
|
<li>Corpo della funzione</li>
|
|
</ul>
|
|
<p>Un esempio:</p>
|
|
<pre class="code literal-block"><span></span>struct Clock {
|
|
|
|
short time;
|
|
|
|
invariant {
|
|
|
|
assert (time &gt; 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
short addReturnTime(Clock c, short n)
|
|
|
|
in {
|
|
n &gt; 0;
|
|
|
|
}
|
|
|
|
body {
|
|
|
|
return c-&gt;time + t;
|
|
|
|
}
|
|
|
|
out (result){
|
|
|
|
result &gt; c-&gt;time;
|
|
|
|
}
|
|
|
|
unittest {
|
|
|
|
auto clock = Clock(60);
|
|
|
|
assert (addReturnTime(clock, 10) == 70);
|
|
|
|
}
|
|
</pre>
|
|
|
|
|
|
<p>Come si nota dall'esempio il supporto ai tests è built-in nel linguaggio e distanti solo una flag in fase di compilazione.</p>
|
|
<h5>Un approccio moderno alle concorrenze</h5>
|
|
<p>Il modello primitivo delle concorrenze in Posix è discutibilmente datato e prono ad errori per il programmatore.</p>
|
|
<p>D di default evita la condivisione di dati fra Threads.
|
|
Fra le varie motivazioni c'è il fatto che questo rifletta più realisticamente l'hardware, ma sicuramente l'obbiettivo finale è la riduzione di bug.</p>
|
|
<p>Non voglio dilungarmi nei dettagli di ogni singolo approccio, ma per completezza D offre out of the box i seguenti modelli:</p>
|
|
<ul>
|
|
<li>Message passing e <a href="http://dist-prog-book.com/chapter/3/message-passing.html">attori</a>, ovvero tutti i dati che vogliono essere condivisi fra thread sono incapsulati in <a href="https://en.wikipedia.org/wiki/Remote_procedure_call">RPC</a>;</li>
|
|
<li>Green threads, come nel mio caso;</li>
|
|
<li>Multi processing, ovvero <code>man 3 fork</code></li>
|
|
<li>TaskPools, ovvero future e promises di Python e Javascript;</li>
|
|
<li><a href="http://moss.csc.ncsu.edu/~mueller/cluster/ps3/SDK3.0/docs/accessibility/sdkpt/cbet_1simdvector.html">SIMD vectorization</a></li>
|
|
</ul>
|
|
<p>Andrei Alexandrescu, uno dei due creatori del linguaggio, dedica un intero capitolo alle concorrenze che potete leggere liberamente <a href="http://www.informit.com/articles/article.aspx?p=1609144">qui</a>.</p>
|
|
<h5>Assenza di dogmatismi</h5>
|
|
<p>Non potrei mai pensare di scrivere un linguaggio di programmazione senza mettere al secondo posto la semplicità.</p>
|
|
<p>Ma chiaramente ancora prima di discutere di semplicità la dobbiamo definire.</p>
|
|
<p>Go e Python sono due linguaggi semplici. Lo sono per la ridotta sintassi (Go in particolare) e perchè attraverso il loro dogmatismo costringono il programmatore ad adottare dei paradigmi di programmazione scelti dai designer di quel linguaggio. E` il motivo per cui in python non abbiamo delle vere lambda e per cui Go non ha le eccezioni.</p>
|
|
<p>In D il programmatore ha libertà piena di scelta. Oltre ad un paradigma di programmazione si può ridefinire la sintassi e evitare il Garbage Collector. Si può in ultimo disattivare tutte le feature del linguaggio che sono @safe e adottare uno stile molto più vicino al C/C++, con tanto di inline asm.</p>
|
|
<h3>Dove iniziare</h3>
|
|
<p>Non posso non concludere un post propagandistico senza indirizzare i più interessati alle prime risorse per imparare D.</p>
|
|
<p>Personalmente consiglio il <a href="http://www.informit.com/store/d-programming-language-9780321635365?w_ptgrevartcl=Concurrency+in+the+D+Programming+Language_1609144">libro</a> di Andrei che offre in particolare moltissimi dettagli sulle motivazioni del design di D. Non ho ancora letto un libro che affrontasse così chiaramente il design di linguaggi di programmazione e i vari compromessi fra performance, semplicità e complessità del compilatore.</p>
|
|
<p>Inoltre il sito della community offre due intro per chi proviene da <a href="https://dlang.org/ctod.html">C</a> e <a href="https://dlang.org/cpptod.html">C++</a>, oltre al classi <a href="https://tour.dlang.org/">tour</a>.</p>
|
|
<p>Inoltre la libreria standard, <a href="https://github.com/dlang/phobos">Phobos</a>, è talmente chiara che solitamente mi trovo a mio agio a consultare direttamente il codice piuttosto che la documentazione online.</p></div></description><category>dlang</category><category>programming</category><category>reddit</category><guid>francescomecca.eu/blog/2018/07/27/addio-reddit/</guid><pubDate>Fri, 27 Jul 2018 00:00:00 GMT</pubDate></item><item><title>Capire il Machine Learning (parte 3)</title><link>francescomecca.eu/blog/2016/11/15/machine-learning-parte3/</link><dc:creator>Francesco Mecca</dc:creator><description><div><h3>Riconoscimento delle immagini</h3>
|
|
<p>Il machine learning viene utilizzato anche per il riconoscimento delle immagini.
|
|
La tipologia di rete neurale utilizzata per questa applicazione è chiamata rete neural a convoluzione ( <strong>convolutional neural networks</strong>), abbreviata CNN.</p>
|
|
<p>Innanzitutto consideriamo che ogni immagine può essere codificata come una matrice di valori</p>
|
|
<p><img alt="pixel" src="francescomecca.eu/wp-content/uploads/2016/8.jpg"></p>
|
|
<p>Vediamo ora quali sono le operazioni compiute da una <strong>CNN</strong> per riconoscere delle immagini.</p>
|
|
<h5>Convoluzione</h5>
|
|
<p>Durante la fase di apprendimento, la rete neurale analizza moltissime immagini (categorizzate) utilizzando dei "filtri", ovvero delle funzioni che mescolate all'input originale permettono di evidenziare dei pattern nell'immagine.
|
|
Questi pattern corrispondono alle caratteristiche proprie di un oggetto (quali possono essere ad esempio per un uccello il becco, le piume, le ali) e nel caso queste sono presenti, possiamo riconoscere l'immagine.</p>
|
|
<p>In questo esempio l'immagine di Wally é mescolata (l'operazione si chiama <strong>convoluzione</strong>) con un filtro "a cerchio" che risponde molto bene a caratteristiche come quella di possedere degli occhi.</p>
|
|
<p><img alt="waldoblue" src="francescomecca.eu/wp-content/uploads/2016/bluefilter.png"></p>
|
|
<p>La <strong>convoluzione</strong> é un'operazione che ha la proprietà di essere indipendente dalla posizione. Non importa la posizione degli occhi, quando applichiamo la <strong>convoluzione</strong> su un'immagine con un filtro "a cerchio" notiamo che gli occhi sono presenti.</p>
|
|
<h5>Subsampling</h5>
|
|
<p>Ogni segnale contiene del "rumore", ovvero degli elementi che la allontanano dal comportamento ideale. </p>
|
|
<p><img alt="ideal" src="francescomecca.eu/wp-content/uploads/2016/ideal.jpg"></p>
|
|
<p><img alt="real" src="francescomecca.eu/wp-content/uploads/2016/real.jpg"></p>
|
|
<p>Attraverso il subsampling possiamo ridurre il rumore e rendere il nostro algoritmo meno suscettibile a queste piccole variazioni; benché l'immagine abbia una risoluzione minore, i pattern rimangono.</p>
|
|
<p><img alt="waldosub" src="francescomecca.eu/wp-content/uploads/2016/sub.png"></p>
|
|
<h5>Connessione completa</h5>
|
|
<p>Alla fine dell'analisi tutte le caratteristiche estrapolate vengono considerate nell'insieme e in questo modo possiamo capire a quale categoria appartiene l'immagine.</p>
|
|
<p>Questo procedimento a livello algoritmo si esplicita con una connessione completa fra tutti i nodi della rete neurale che possono poi restituire l'output (probabilità che l'immagine appartenga ad una determinata categoria).</p>
|
|
<h5>Fase di rinforzo</h5>
|
|
<p>Durante il training é presente un'ultima fase (o strato), chiamato più propriamente <strong>loss layer</strong>. Questo strato provvede a dare un <strong>feedback</strong> alla rete neurale analizzando l'output in relazione ai dati di partenza (ovvero le immagini già categorizzate).</p></div></description><category>AI</category><category>algoritmi genetici</category><category>Genetic algorithm</category><category>Neural networks</category><category>PesceWanda</category><category>programming</category><category>reti neurali</category><guid>francescomecca.eu/blog/2016/11/15/machine-learning-parte3/</guid><pubDate>Tue, 15 Nov 2016 00:00:00 GMT</pubDate></item><item><title>Capire il Machine Learning (parte 2)</title><link>francescomecca.eu/blog/2016/11/11/machine-learning-parte2/</link><dc:creator>Francesco Mecca</dc:creator><description><div><p>Nel precedente <a href="francescomecca.eu/pescewanda/2016/11/10/machine-learning-intro/">post</a> abbiamo preso in considerazione una rete neurale molto basica.
|
|
Proviamo ora ad approfondire il concetto aggiungendo una proprietà fondamentale, la memoria.</p>
|
|
<h3>Memoria e Stati</h3>
|
|
<p>La rete neurale che abbiamo modellato non ha alcun tipo di memoria. con gli stessi dati di input, l'output è (quasi certamente) lo stesso.
|
|
Possiamo ampliare il nostro modello introducendo il concetto di <em>stato</em>.</p>
|
|
<p>Poniamo il problema della vendita delle auto in questo modo:
|
|
immaginiamo di avere un algoritmo di machine learning che valuti le auto e faccia delle offerte di vendita che conseguentemente vengono valutate da delle persone e accettate oppure rifiutate.
|
|
Ogni volta che una proposta viene accettata la teniamo in memoria e nell'aggiustare i pesi per la seguente offerta, teniamo in considerazione la validità dei pesi usati in precedenza.
|
|
In altre parloe, in ogni operazione di valutazione dell'auto salviamo lo stato dell'offerta precedente (rifiutata o accettata) e la consideriamo quando vogliamo proporre l'offerta successiva.
|
|
Abbiamo in questo modo una <em>recurrent neural network</em> (RNN) dove ogni stato precedente viene utilizzato per modificare l'output dell'algoritmo.</p>
|
|
<h5>Generazione di testo attraverso le RNN</h5>
|
|
<p>Immaginiamo di voler creare un algoritmo che utilizzando una RNN possa generare del testo o prevedere quale sarà il prossimo carattere inserito (come nel caso del T9).</p>
|
|
<p>Inizialmente dobbiamo permettere alla RNN di analizzare almeno qualche sample del nostro testo.
|
|
La RNN analizza carattere dopo carattere e costruisce un grande grafo dove collega ogni carattere al suo successivo.
|
|
Inizialmente l'output della nostra RNN non ha alcun valore:
|
|
in questo esempio, abbiamo l'output di una RNN allenata attraverso Guerra e Pace:</p>
|
|
<pre class="code literal-block"><span></span>tyntd-iafhatawiaoihrdemot lytdws e ,tfti, astai f ogoh eoase rrranbyne 'nhthnee e
|
|
plia tklrgd t o idoe ns,smtt h ne etie h,hregtrs nigtike,aoaenns lngty
|
|
</pre>
|
|
|
|
|
|
<p>L'output prodotto dalla RNN, benchè non abbia nessun valore per un umano, viene analizzato dalla RNN per arricchire il grafo costituito inizialmente dai caratteri (e le loro relazioni) di Guerra e Pace.
|
|
Questo procedimento continua all'infinito.
|
|
Ogni volta che la RNN produce dell'output lo salva e lo riutilizza come input in maniera ricorsiva per generare nuovo output.</p>
|
|
<p>Dopo 300 iterazioni abbiamo un output di questo tipo:</p>
|
|
<pre class="code literal-block"><span></span>"Tmont thithey" fomesscerliund
|
|
Keushey. Thom here
|
|
sheulke, anmerenith ol sivh I lalterthend Bleipile shuwy fil on aseterlome
|
|
coaniogennc Phe lism thond hon at. MeiDimorotion in ther thize."
|
|
</pre>
|
|
|
|
|
|
<p>Dopo quasi mille:</p>
|
|
<pre class="code literal-block"><span></span>Aftair fall unsuch that the hall for Prince Velzonski's that me of
|
|
her hearly, and behs to so arwage fiving were to it beloge, pavu say falling misfort
|
|
how, and Gogition is so overelical and ofter.
|
|
</pre>
|
|
|
|
|
|
<p>Duemila:</p>
|
|
<pre class="code literal-block"><span></span>"Why do what that day," replied Natasha, and wishing to himself the fact the
|
|
princess, Princess Mary was easier, fed in had oftened him.
|
|
Pierre aking his soul came to the packs and drove up his father-in-law women.
|
|
</pre>
|
|
|
|
|
|
<p>Possiamo notare come l'output migliori visibilmente.</p>
|
|
<p>Nel prossimo post tratterò una variante di rete neurale utilizzata per classificare e riconoscere immagini.</p></div></description><category>AI</category><category>algoritmi genetici</category><category>Genetic algorithm</category><category>Neural networks</category><category>PesceWanda</category><category>programming</category><category>reti neurali</category><guid>francescomecca.eu/blog/2016/11/11/machine-learning-parte2/</guid><pubDate>Fri, 11 Nov 2016 00:00:00 GMT</pubDate></item><item><title>Capire il Machine Learning (parte 1)</title><link>francescomecca.eu/blog/2016/11/10/machine-learning-intro/</link><dc:creator>Francesco Mecca</dc:creator><description><div><p>Questo è il primo di una serie di post che hanno l'obbiettivo di fornire una breve e generale introduzione al mondo del machine learning e delle intelligenze artificiali più in generale.
|
|
Mi auguro che questa breve introduzione al mondo dell'apprendimento automatico sia una sorta di vaccino contro il sensazionalismo mediatico e la disinformazione che negli ultimi anni fanno da contorno al progresso in questo settore.</p>
|
|
<h3>Non c'è risultato senza algoritmo</h3>
|
|
<p>Nelle scienze informatiche un algoritmo è un insieme di istruzioni che partendo da uno stato iniziale (input) permette di arrivare ad uno stato finale (output) attraverso una serie di step logici.
|
|
Ogni algoritmo utilizza una logica propria e specifica per il problema di cui si occupa.
|
|
Nel caso del machine learning l'algoritmo non viene progettato in base al tipo di problema bensì vengono utilizzati algoritmi generici adattabili attraverso dei parametri.
|
|
L'algoritmo di machine learning analizza i vari parametri e i dati che riceve "in pasto" al fine di raggiungere lo stato di output ottimale.
|
|
L'output ottimale è la migliore approssimazione di un risultato teorico che si raggiunge nella fase di "training".</p>
|
|
<h3>La macchina impara</h3>
|
|
<p>Nella fase di allenamento o di apprendimento, il "training", si possono adottare due tecniche differenti: apprendimento con supervisione ( <strong>supervised learning</strong> ) e apprendimento senza supervisione ( <strong>unsupervised learning</strong> ).</p>
|
|
<h5>Supervised Learning</h5>
|
|
<p>Immaginiamo di dover valutare il prezzo di un'automobile usata senza essere esperti in materia.
|
|
Noi abbiamo questi dati:</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>PREZZO ORIGINALE</th>
|
|
<th align="center">Km percorsi</th>
|
|
<th align="center">stato di usura</th>
|
|
<th align="right">PREZZO FINALE</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>50 000</td>
|
|
<td align="center">120 000</td>
|
|
<td align="center">lieve</td>
|
|
<td align="right">40 000</td>
|
|
</tr>
|
|
<tr>
|
|
<td>30 000</td>
|
|
<td align="center">150 000</td>
|
|
<td align="center">notevole</td>
|
|
<td align="right">8 000</td>
|
|
</tr>
|
|
<tr>
|
|
<td>20 000</td>
|
|
<td align="center">80 000</td>
|
|
<td align="center">lieve</td>
|
|
<td align="right">15 000</td>
|
|
</tr>
|
|
<tr>
|
|
<td>20 000</td>
|
|
<td align="center">120 000</td>
|
|
<td align="center">notevole</td>
|
|
<td align="right">...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p>Dalla tabella possiamo dedurre la caratteristica fondamentale del <strong>supervised learning</strong>: abbiamo due vettori (serie di dati) di input (prezzo originale) e di output (prezzo finale) che hanno una correlazione certa e valida.</p>
|
|
<p>Possiamo dedurre intuitivamente quale sarà il prezzo dell'ultima auto se analizziamo i dati precedenti.
|
|
Questo è quello che succede nel caso del <strong>supervised learning</strong>.
|
|
Un algoritmo di machine learning che utilizza il <strong>supervised learning</strong> estrapolerà la relazione fra i vari dati e in questo modo potrà ottenere un determinato output partendo dai dati di input.
|
|
Possiamo capire già da ora che nel caso ci fossero dati che "inquinano" il nostro data set, come ad esempio il colore dell'auto, l'algoritmo non sarà capace di fare un'analisi corretta.</p>
|
|
<p>La precisione della soluzione dipende dalla quantità di dati e dall'influenza che questi hanno nella situazione reale.</p>
|
|
<h2>Unsupervised Learning</h2>
|
|
<p>Nel caso di unsupervised learning ci troviamo di fronte agli stessi dati ma senza la possibilità di conoscere il prezzo finale.</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>PREZZO ORIGINALE</th>
|
|
<th align="center">Km percorsi</th>
|
|
<th align="right">stato di usura</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>50 000</td>
|
|
<td align="center">120 000</td>
|
|
<td align="right">lieve</td>
|
|
</tr>
|
|
<tr>
|
|
<td>30 000</td>
|
|
<td align="center">150 000</td>
|
|
<td align="right">notevole</td>
|
|
</tr>
|
|
<tr>
|
|
<td>20 000</td>
|
|
<td align="center">80 000</td>
|
|
<td align="right">lieve</td>
|
|
</tr>
|
|
<tr>
|
|
<td>20 000</td>
|
|
<td align="center">120 000</td>
|
|
<td align="right">notevole</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p>Non siamo capaci di stabilire il prezzo finale attraverso l'unsupervised learning, ma possiamo stabilire dei pattern fra i vari dati.
|
|
Non c'è nessun tipo di feedback (il prezzo finale) che possa aiutarci a capire se il risultato sia giusto ma possiamo analizzare le notevoli relazioni fra i dati.</p>
|
|
<h2>Machine Learning e intelligenza</h2>
|
|
<p>Concentriamoci sul <strong>supervised learning</strong>.
|
|
Nel caso della vendita dell'automobile, abbiamo un semplice problema con una soluzione lineare di questo tipo:
|
|
<em>prezzo finale</em> = <em>prezzo originale</em> + <em>km percorsi</em> + stato di usura</p>
|
|
<p>Ovviamente ogni incognita nella nostra equazione influisce diversamente sul prezzo finale e quindi possiamo riscriverla come:
|
|
<em>prezzo finale</em> = A <em><em>prezzo originale</em> + B </em><em>km percorsi</em> + C * stato di usura</p>
|
|
<p>Se pensiamo ad un algoritmo possiamo ragionare in questo modo:</p>
|
|
<pre class="code literal-block"><span></span><span class="n">funzione</span><span class="o">:</span> <span class="n">calcola_prezzo_auto</span><span class="o">:</span>
|
|
<span class="n">parametri</span><span class="o">:</span> <span class="n">prezzo_originale</span><span class="o">,</span> <span class="n">km_percorsi</span><span class="o">,</span> <span class="n">stato_usura</span>
|
|
<span class="n">variabili</span><span class="o">:</span> <span class="n">prezzo_finale</span> <span class="o">=</span> <span class="mi">0</span>
|
|
|
|
<span class="n">prezzo</span> <span class="n">finale</span> <span class="o">=</span> <span class="n">prezzo_originale</span> <span class="o">*</span> <span class="mf">0.804246</span>
|
|
<span class="n">prezzo</span> <span class="n">finale</span> <span class="o">=</span> <span class="n">prezzo_finale</span> <span class="o">+</span> <span class="n">km_percorsi</span> <span class="o">*</span> <span class="o">-</span><span class="mf">0.000125</span>
|
|
<span class="n">prezzo</span> <span class="n">finale</span> <span class="o">=</span> <span class="n">prezzo_finale</span> <span class="o">+</span> <span class="n">stato_usura</span> <span class="o">*</span> <span class="o">-</span><span class="mi">2500</span>
|
|
</pre>
|
|
|
|
|
|
<p>I valori di quelle costanti, stabilite casualmente nell'esempio, sono chiamate <em>pesi</em> e servono a stimare il prezzo finale.
|
|
Una volta stabiliti i pesi, il nostro algoritmo di supervised learning applica questi pesi ai dati originali e ne valuta l'errore:</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>PREZZO ORIGINALE</th>
|
|
<th align="center">Km percorsi</th>
|
|
<th align="center">stato di usura</th>
|
|
<th align="center">PREZZO FINALE</th>
|
|
<th align="right">PREZZO STIMATO</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>50 000</td>
|
|
<td align="center">120 000</td>
|
|
<td align="center">lieve</td>
|
|
<td align="center">40 000</td>
|
|
<td align="right">374888</td>
|
|
</tr>
|
|
<tr>
|
|
<td>30 000</td>
|
|
<td align="center">150 000</td>
|
|
<td align="center">notevole</td>
|
|
<td align="center">8 000</td>
|
|
<td align="right">16000</td>
|
|
</tr>
|
|
<tr>
|
|
<td>20 000</td>
|
|
<td align="center">80 000</td>
|
|
<td align="center">lieve</td>
|
|
<td align="center">15 000</td>
|
|
<td align="right">13492</td>
|
|
</tr>
|
|
<tr>
|
|
<td>20 000</td>
|
|
<td align="center">120 000</td>
|
|
<td align="center">notevole</td>
|
|
<td align="center">...</td>
|
|
<td align="right">10988</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p>Una volta valutato l'errore e la distanza dal prezzo finale, l'algoritmo di machine learning modifica i pesi di conseguenza e ripete la procedura fino ad arrivare al risultato che più si avvicina ai dati iniziali.
|
|
<img alt="rete1" src="francescomecca.eu/wp-content/uploads/2016/reteneurale1.jpg">
|
|
Ci sono varie funzioni che stimano l'errore e permettono di correggere i pesi o metodi che restringono lo spazio di ricerca fino a convergere alla soluzione, ovvero i pesi cercati.</p>
|
|
<h3>Reti Neurali</h3>
|
|
<p>Ora, come possiamo immaginare, il prezzo di un'auto ha molti fattori che si aggiungono a quelli elencati nell'esempio.
|
|
Non solo, ma molti problemi non hanno una soluzione lineare, ovvero una soluzione che si può semplicemente esprimere attraverso una funzione che aggiunge e moltiplica i valori una sola volta.</p>
|
|
<p>Possiamo arricchire l'esempio dell'automobile immaginando di avere più set di pesi e di ripetere il procedimento più volte:</p>
|
|
<pre class="code literal-block"><span></span>|---------------------------------------------------|
|
|
| PREZZO_ORIG * peso A1 -----&gt; |
|
|
| KM_PERCORSI * peso B1 -----&gt; |
|
|
| STATO_USURA * peso C1 -----&gt; |
|
|
| PREZZO FINALE STIMATO 1|
|
|
|---------------------------------------------------|
|
|
|
|
| --------------------------------------------------|
|
|
| PREZZO_ORIG * peso A2 -----&gt; |
|
|
| KM_PERCORSI * peso B2 -----&gt; |
|
|
| STATO_USURA * peso C2 -----&gt; |
|
|
| PREZZO FINALE STIMATO 2|
|
|
|---------------------------------------------------|
|
|
|
|
| --------------------------------------------------|
|
|
| PREZZO_ORIG * peso A3 -----&gt; |
|
|
| KM_PERCORSI * peso B3 -----&gt; |
|
|
| STATO_USURA * peso C3 -----&gt; |
|
|
| PREZZO FINALE STIMATO 3|
|
|
|---------------------------------------------------|
|
|
</pre>
|
|
|
|
|
|
<p>E ora immaginiamo di combinare ogni PREZZO FINALE STIMATO in un'ultimo risultato:</p>
|
|
<pre class="code literal-block"><span></span>| --------------------------------------------------|
|
|
| PREZZO_1 * peso X -----&gt; |
|
|
| PREZZO_2 * peso Y -----&gt; |
|
|
| PREZZO_3 * peso Z -----&gt; |
|
|
| PREZZO FINALE DEF |
|
|
|---------------------------------------------------|
|
|
</pre>
|
|
|
|
|
|
<p>Questa é, seppur molto basica, una rete neurale.
|
|
Proviamo a visualizzarla in un'immagine dove i box arancioni sono i nodi di input e i rossi sono i nodi "nascosti" e temporanei.</p>
|
|
<p><img alt="rete2" src="francescomecca.eu/wp-content/uploads/2016/reteneurale2.jpg"></p>
|
|
<p>In una rete neurale (<em>neural networks</em>) abbiamo:</p>
|
|
<ul>
|
|
<li>
|
|
<p>i neuroni: la funzione di stima e i set di pesi;</p>
|
|
</li>
|
|
<li>
|
|
<p>le catene: i collegamenti fra neuroni che permettono di valutare il prezzo più volte.</p>
|
|
</li>
|
|
</ul>
|
|
<p>Nel prossimo <a href="francescomecca.eu/pescewanda/2016/11/11/machine-learning-PARTE2">post</a> cercherò di approfondire il concetto di rete neurale con degli esempi di applicazioni concrete.</p></div></description><category>AI</category><category>algoritmi genetici</category><category>Genetic algorithm</category><category>Neural networks</category><category>PesceWanda</category><category>programming</category><category>reti neurali</category><guid>francescomecca.eu/blog/2016/11/10/machine-learning-intro/</guid><pubDate>Thu, 10 Nov 2016 00:00:00 GMT</pubDate></item><item><title>Arduino Uno as HID keyboard</title><link>francescomecca.eu/blog/2016/7/5/arduino_keyboard/</link><dc:creator>Francesco Mecca</dc:creator><description><div><p>Turin is the hometown of Arduino. I have been at the <a href="http://fablabtorino.org/">fablab</a> multiple times but I had to come all the way to America to get my hands on a simple Arduino Uno.</p>
|
|
<p>For 60$ I bought a cheap (but still good!) mechanical keyboard by Qisan, a clone of the Arduino Uno and a USB host shield.</p>
|
|
<p>Given that is 3 years since I have been using a dvorak layout and it's a pain to change layout on every machine that you have to use.
|
|
You can imagine that given this three pieces of hardware together I put together an hardware key mapper for the keyboard.</p>
|
|
<p>I have never had experience with Arduino before but it was not that difficult to make it do simple things like blinking the led or send signal through to a serial monitor.</p>
|
|
<p>It took me half an hour to wear down all my excitement: the USB Host Shield library broke all the compatibility with the similar project I found wandering online.</p>
|
|
<p>In particular <a href="http://hunt.net.nz/users/darran/">this blog</a> has the most precious information and the guy wrote a HID driver that allows the Uno to be seen as a HID device.</p>
|
|
<p>It was a noob error but I didn't checked the various arduino alternatives and I discovered late that just a few have the HID capabilities that would make this work easier. I should have bought and Arduino Due or Leonardo maybe.</p>
|
|
<p>Also, the various guides about flashing with a dfu tool are specific to older models of the Uno and it took me some time to figure the name of the new components so that I could flash a new firmware.</p>
|
|
<h3>A small journey in the Arduino world</h3>
|
|
<p>It feels pretentious to write a little guide for this kind of work, given also the fact that I have roughly 10 hours of experience with the Arduino. But the other resources are really outdated so I hope this piece can be useful to someone out there.</p>
|
|
<p>All the files I have used today are on <a href="http://francescomecca.eu:3000/pesceWanda/arduino_HID_keyboard">my repos</a> and I included also an outdated version of the USB Host Shield library that I used.</p>
|
|
<p>The original code from this <a href="http://hunt.net.nz/users/darran/weblog/c6f35/Arduino_USB_Keyboard_Passthrough.html">blog post</a> works like a charm but just as a simple passthrough.</p>
|
|
<p>It was not difficult at all to examine the code:
|
|
during each loop of the iteration a char array gets read from the shield and if it is contains information Arduino with the Serial.Write method send the data to the host.</p>
|
|
<p>The buffer array is a simple array of length 8 and the first two positions are reserved. In particular the first one represent the various modifier keys.</p>
|
|
<p>The dvorak layout has the same pairs as the US layout but eventually I got used to having the '@' where at the same place of 'Q' (qwerty) and '"' over the '2'.
|
|
Also, I am an avid vim user (I should thank Simone Basso for that) and I swapped some keys on the new 65 keys keyboard.
|
|
The modifier bit at the beginning of the array came in handy for my code.</p>
|
|
<p>An hardware key remapper is a simple but long switch C statement but I decided to consider also the modifier bit: in this way certain keys like the Window (UGH!) key is mapped to a different layer of keys.
|
|
I got all the codes for the HID events <a href="http://www.freebsddiary.org/APC/usb_hid_usages.php">here</a>.</p>
|
|
<p>The process of flashing the code on the Uno goes like this:</p>
|
|
<ul>
|
|
<li>write the looping code;</li>
|
|
<li>push it to the Arduino using the IDE;</li>
|
|
<li>shortcircuit the board so that it goes in DFU mode;</li>
|
|
<li>flash the .hex HID firmware;</li>
|
|
<li>try your code;</li>
|
|
<li>repeat until it's right.</li>
|
|
</ul>
|
|
<p><img alt="Everything fits in one picture" src="francescomecca.eu/wp-content/uploads/2016/IMG_20160706_011304.jpg"></p>
|
|
<h3>Flashing the firmware</h3>
|
|
<p>The firmware is in my repo but I got it from (here)[http://hunt.net.nz/users/darran/weblog/a6d52/Arduino_UNO_Keyboard_HID_version_02.html].
|
|
The tool I used to flash it is dfu-programmer (version 0.62).
|
|
Every time you want to flash a new firmware the Arduino must be put in DFU mode (you can see the difference with lsusb).
|
|
To do that simply create a shortcircuit using a small metal wire on the two pins near the reset button and a led will blink.
|
|
This <a href="https://www.youtube.com/watch?v=E8XyRwXQr8Q">video</a> shows the method briefly (no real need for a jumper).
|
|
The commands are the following and there is no risk to brick the Uno:</p>
|
|
<pre class="code literal-block"><span></span>dfu-programmer atmega16u2 erase
|
|
dfu-programmer atmega16u2 flash Arduino-keyboard-0.2.hex
|
|
dfu-programmer atmega16u2 reset
|
|
</pre>
|
|
|
|
|
|
<p>After each flashing the device needs to be disconnected once. Of course you can flash the original firmware back. It is included in my repo or on the official ones.</p>
|
|
<p><img alt="Arduino and the shield" src="francescomecca.eu/wp-content/uploads/2016/IMG_20160706_011143.jpg"></p>
|
|
<p>That's it, as you can see is not difficult at all. The worst part is gathering the various info that are left dormant in blogs or forums.</p></div></description><category>Arduino</category><category>arduino uno</category><category>HID arduino</category><category>mechanical keyboard</category><category>PesceWanda</category><category>programming</category><guid>francescomecca.eu/blog/2016/7/5/arduino_keyboard/</guid><pubDate>Tue, 05 Jul 2016 00:00:00 GMT</pubDate></item><item><title>Interpolation using a genetic algorithm</title><link>francescomecca.eu/blog/2016/5/15/genetic-alg/</link><dc:creator>Francesco Mecca</dc:creator><description><div><p>This weekend I was in Milan to get a visa and I had the opportunity to work with a friend, Michele, on genetic algorithms.
|
|
It was the first time I dig up in such field and it was very exciting.
|
|
In this post I want to explain some bits of our work.</p>
|
|
<h3>A brief introduction to GA</h3>
|
|
<p>A genetic algorithm is a search/optimization algorithm that uses an heuristic approach to reduce the search space and evolve gradually to a solution.</p>
|
|
<h5>Population</h5>
|
|
<p>It is an algorithm that has its root in the theory of natural selectioni by Charles Darwin.
|
|
The main components of a GA are:</p>
|
|
<ul>
|
|
<li>the population, that concentrate all the available solutions at a given time;</li>
|
|
<li>the fitness function, that gives an approximation of the quality of the solution codified by a given member of the population.</li>
|
|
</ul>
|
|
<p>In a GA the first thing to do is to generate a population.</p>
|
|
<p>A population is a group of objects with given attributes, usually a string, and they contains in some form the solution (usually inside a string); the first population is randomly generated and contains a big number of solutions, but not every solution (this is not a bruteforce approach).</p>
|
|
<p>After this step the fitness functions evaluates the quality of every solution that a given member carries: the evaluation should be considered from a bottom up point of view.</p>
|
|
<h5>Reproduction</h5>
|
|
<p>Now, as in Darwin's theory of evolution, the member of the population are going to "reproduce": two members are going to be coupled to generate a new member of the second generation and every child member will contain a solution that is the product of the original genes of their parent members.</p>
|
|
<p>This time the reproduction of the population into a second one is not entirely random. The fitness function gives us an approximation of the quality of every gene that a member carries and by the rule of the "survival by the fittest" the probability that a member is going to reproduce with another one is proportional to the quality of its genes.</p>
|
|
<p>When we have a second generation of members we can recur on our GA and generate a third generation. From this point we can recur until we converge to a solution that is common to every member, or at least that is suited to our needs.</p>
|
|
<h5>Mutation</h5>
|
|
<p>Actually, in some cases, a mutation function can be added, so that, like in real world, some times the genes are "scrambled" indipendently from the fitness function.</p>
|
|
<p>There is more to a GA, for example we could talk about possible ways of storing the genes inside a member or when to use mutation, anyway I want to stop here and continue with an analysis of my problem.</p>
|
|
<h3>Interpolating a function using a GA</h3>
|
|
<p>Me and Michele decided to spend some time developing a little python script to explore GA capabilities and we decided to interpolate some points on a cartesian plane.</p>
|
|
<p>Our program, that is available <a href="http://francescomecca.eu:3000/pesceWanda/interpol_genetica">here</a> uses a class to define the various members of the population and a string for the genes, a class as well for the points on the plane.</p>
|
|
<p>The fitness function is not as precise as it should be because this is only a proof of concept:</p>
|
|
<p>.. code:: python</p>
|
|
<pre class="code literal-block"><span></span>mutationProbability = 0.1
|
|
rangeLimit = 5
|
|
def fitness(item, pointList, n):
|
|
value = 0
|
|
for p in pointList:
|
|
y = 0
|
|
for i in range(n):
|
|
y += item.gene[i] * pow(p.x, i)
|
|
result = 1 - (abs (p.y - y) / rangeLimit)
|
|
if result &lt; 0:
|
|
result = 0
|
|
value += result
|
|
return value / n
|
|
</pre>
|
|
|
|
|
|
<p>item is just a member of the population, poinList is the list of points and n is the number of points (n - 1 is the grade of the function).</p>
|
|
<pre class="code literal-block"><span></span>for i in range(n):
|
|
y += item.gene[i] * pow(p.x, i)
|
|
</pre>
|
|
|
|
|
|
<p>this piece of code gives us the value of the function encoded in the genes in the points of pointList;</p>
|
|
<pre class="code literal-block"><span></span>result = 1 - (abs (p.y - y) / rangeLimit)
|
|
if result &lt; 0:
|
|
result = 0
|
|
</pre>
|
|
|
|
|
|
<p>while here the script stores 1 - the previous result because if the GA has yield a good result there should be distance = 0 from the function evaluated and the points; If this is the case, the fitness function should attribute the highest possible reproduction probability for that member.
|
|
At the end the fitness function returns the total value over the number of points evaluated.</p>
|
|
<p>As you can see this fitness function is by no means an optimal one. The reproduction probability is higher for functions that crosses some points and are really distant from others rather than for functions that are closer to every point but crosses none.
|
|
Anyway for simple cases the GA yields good results, as an example for points (0 0), (1 4), (2 9) one of the member with the highest reproduction probability has this function in its genes:</p>
|
|
<pre class="code literal-block"><span></span>-0.0487839869993989 * x^0 + 4.600339125358671 * x^1 + -0.2780958075230644 * x^2
|
|
</pre>
|
|
|
|
|
|
<p>that crosses this points: (0 -0.0488), (1 4.2735), (2 8.0395) given 80 iterations, initial population of 600 members and a two digit approximation.</p>
|
|
<p>For a more precise computation a higher population size and a really high number of iterations should be used.</p></div></description><category>AI</category><category>Genetic algorithm</category><category>PesceWanda</category><category>programming</category><category>python</category><guid>francescomecca.eu/blog/2016/5/15/genetic-alg/</guid><pubDate>Sun, 15 May 2016 00:00:00 GMT</pubDate></item><item><title>Kyuss Music Player</title><link>francescomecca.eu/blog/2016/4/17/kpd-player/</link><dc:creator>Francesco Mecca</dc:creator><description><div><p>For a long time I have been using Clementine music player on my workstation. Recently I reinstalled Gentoo on my desktop and I wanted to avoid installing QT libraries of any sort.
|
|
So I switched to <a href="https://www.musicpd.org/">mpd</a> and I have fallen in love with it. It is very flexible, fast and enriched by a lot of community software.
|
|
For some weeks I used mpc client as my primary client for mpd but I was not satisfied with it. Even though it is pretty minimal but packed with every feature mpd permits, the search feels uncomfortable because is case sensitive and need artist, album, etc. flags before any entry.
|
|
This is why I have written kpd together with Francesco Gallà</p>
|
|
<h3>Kyuss Player Client</h3>
|
|
<p>kpd is an acronym for Kyuss Player Client because we have been listening only to <a href="https://en.wikipedia.org/wiki/Kyuss">Kyuss</a> while programming this client.
|
|
We have reimplemented the search functions to suit our habits. No more case sensitive, optional 'artist, album, title' flags.
|
|
kpd accepts only one string as the search argument and implements optional filter arguments to narrow the search in a grep like way.
|
|
I welcome you to read the <a href="http://francescomecca.eu:3000/pesceWanda/kpd">readme</a> in my git to understand how the search works.
|
|
Anyway in this post I want to explain bits of the code.</p>
|
|
<h4>Main</h4>
|
|
<p>The main kpd file invoked when the command is run in the console is kpd.py
|
|
The most interesting part in this file IMHO is these lines:</p>
|
|
<p>.. code:: python</p>
|
|
<pre class="code literal-block"><span></span> for el in argsOrder:
|
|
if dictArgs[el] != False:
|
|
client.update_status ()
|
|
methodToCall = getattr (util, el)
|
|
retUtil = methodToCall (client, dictArgs[el], searchRes)
|
|
</pre>
|
|
|
|
|
|
<p>argsOrder is a list of the arguments on the command line in the order the user wrote them.
|
|
kpd uses a dictionary to store for every argument the corrispective string for the function that will be invoked using getattr.
|
|
In this way any argument can be added to the main file without writing any other line of code. WE used this method to avoid using switch alike solutions.</p>
|
|
<h4>Util</h4>
|
|
<p>The util.py source file is a pretty easy source file to read. It contains every function that can be invoked by command line arguments. Every function has the same 'prototypes' so that they can be called using the method explained above.
|
|
To implement <code>no-output</code> and <code>output</code> function I have used a class:
|
|
to suppress the output on the console the program assign to <em>sys.stdout</em> a dummy class that save the original stdout on a variable and replaces write and flush functions so that they are just pass. and no output is written.
|
|
To permit output after suppression the program just reassing the original value to sys.stdout.</p>
|
|
<h4>Database Search</h4>
|
|
<p>In MPDdatabase.py we have written the search functions.
|
|
Originally we intended to just read and import in a dictionary the whole mpd database that is stored compressed in the home directory.
|
|
This list of dictionaries stores every entry related to the song and if any of them matches the search string or the filter string (considering also flags if any) the related song is printed on the output and saved in a list so it can be added by the add function.
|
|
This approach result very efficent in term of precision but it lacked speed. For a database of about 77 thousand songs (about 550k lines) a search query could last almost 2 seconds.
|
|
To improve the speed of the search we used the pickle module. The pickle module allows kpd to dump the data structure used to store the database in memory on a file that can be read easily by using the <code>pickle.load</code> function.
|
|
In this way the search lasts about 40 milliseconds on the same database that wastes about 16MiB of memory on disk.</p>
|
|
<h3>Conclusion</h3>
|
|
<p>This was really fun. It was our first hand on python project and the first real program we have written since we started learning programming at our university.
|
|
I discovered that programming allows me to relax and that is really cool to have custom software for activities you do every day.
|
|
The source for our program is stored in my git <a href="http://francescomecca.eu:3000/pesceWanda/kpd">here</a> and you are free to modify it.</p></div></description><category>mpd</category><category>music player</category><category>PesceWanda</category><category>programming</category><category>python</category><guid>francescomecca.eu/blog/2016/4/17/kpd-player/</guid><pubDate>Sun, 17 Apr 2016 00:00:00 GMT</pubDate></item></channel></rss> |