<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Mind Unpacked &#187; Intelligenza artificiale</title>
	<atom:link href="http://mindunpacked.com/tag/intelligenza-artificiale/feed/" rel="self" type="application/rss+xml" />
	<link>http://mindunpacked.com</link>
	<description>informatica.elettronica.chimica.new stuff</description>
	<lastBuildDate>Tue, 22 Dec 2009 17:35:13 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<image>
<link>http://mindunpacked.com</link>
<url>http://mindunpacked.com/wp-content/plugins/maxblogpress-favicon/icons/favicon-2.ico</url>
<title>Mind Unpacked</title>
</image>
		<item>
		<title>Robocode: guerre tra robot in Java</title>
		<link>http://mindunpacked.com/2009/robocode-guerre-tra-robot-in-java/</link>
		<comments>http://mindunpacked.com/2009/robocode-guerre-tra-robot-in-java/#comments</comments>
		<pubDate>Fri, 16 Jan 2009 19:48:56 +0000</pubDate>
		<dc:creator>Francesco</dc:creator>
				<category><![CDATA[Informatica]]></category>
		<category><![CDATA[crobots]]></category>
		<category><![CDATA[Intelligenza artificiale]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[robocode]]></category>

		<guid isPermaLink="false">http://mindunpacked.com/?p=378</guid>
		<description><![CDATA[Molti di voi probabilmente conosceranno o avranno sentito parlare di CRobots, il gioco di programmazione in cui gli sfidanti devono creare, usando un sottoinsieme delle istruzioni del linguaggio C, un robot che distrugga tutti gli altri robot (creati da altri programmatori) presenti nell&#8217;arena. Bene, Robocode e&#8217; un gioco molto simile ma con alcune differenze: i [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Molti di voi probabilmente conosceranno o avranno sentito parlare di <strong>CRobots</strong>, il gioco di programmazione in cui gli sfidanti devono creare, usando un sottoinsieme delle istruzioni del linguaggio C, un robot che distrugga tutti gli altri robot (creati da altri programmatori) presenti nell&#8217;arena. Bene, <strong>Robocode</strong> e&#8217; un gioco molto simile ma con alcune differenze: i robot si scrivono in Java e non ci sono limiti alla fantasia del programmatore come avveniva in CRobots (perche&#8217; usava solo un set limitato di istruzioni) rendendo possibile creare anche robot dalla complessa intelligenza artificiale!<br />
<span id="more-378"></span><br />
Sul <a href="http://robocode.sourceforge.net">sito ufficiale</a> di <strong>Robocode</strong> trovate molte info e l&#8217;installer in Java. Vediamo subito come creare il nostro primo robot dopo aver effettuato l&#8217;installazione. Subito dopo aver aperto Robocode, aprite il menu Robot e cliccate su Editor; si aprira&#8217; una nuova finestra, cioe&#8217; l&#8217;editor tramite il quale possiamo programmare i nostri robot. Cliccate su File-&gt;New-&gt;Robot. Battezzate la vostra creature e scegliete un nome o una sigla caratteristica per tutti i vostri robot (il programma crea una cartella contenente tutti i robot che creerete usando come nome la seconda sigla che inserite).</p>
<p style="text-align: justify;">Quello che vedete ora nell&#8217;editor e&#8217; il codice di base per un robot molto semplice che non fa altro che muoversi avanti e indietro e girare il suo cannone di 360 gradi, sparando quando vede un nemico. Ma analizziamo piu&#8217; da vicino il codice:</p>
<pre class="brush: java;">
public class MURobot extends Robot {
</pre>
<p>Creiamo una classe che eredita tutti gli attributi e i metodi dalla classe di base Robot.</p>
<pre class="brush: java;">
	/* Qui possiamo dichiarare tutte le variabili utilizzate dal nostro robot,
	 * in questo caso non ne abbiamo.
	 */
	public void run() {
		while(true) {
			ahead(100);
			turnGunRight(360);
			back(100);
			turnGunRight(360);
		}
	}
</pre>
<p style="text-align: justify;">Dopo aver dichiarato le eventuali variabili utilizzate all&#8217;interno della classe, vediamo il metodo run(). Questo metodo viene chiamato ogni qualvolta inizia la battaglia. All&#8217;interno di run() si trova quasi sempre un ciclo infinito: se l&#8217;esecuzione di run() termina, infatti, il nostro robot non fara&#8217; altro che stare fermo e sparare ai robot che rientrano nel suo campo visivo. Se dobbiamo svolgere delle azioni particolari, come il posizionamento in una particolare zona della mappa, conviene inserirle prima di entrare nel ciclo infinito.<br />
All&#8217;interno del ciclo il robot si muove in avanti (ahead(100)) fa un giro completo del cannone, torna indietro, fa lo stesso giro e cosi&#8217; finche&#8217; non viene distrutto o distrugge tutti i robot nell&#8217;arena.</p>
<p style="text-align: justify;">
<pre class="brush: java;">
	public void onScannedRobot(ScannedRobotEvent e) {
		fire(1);
	}
</pre>
<p><center><script type="text/javascript">
heyos_ad_user = 11334;
heyos_ad_type = "G";
heyos_ad_format = "1";
heyos_color_border = "23292b";
heyos_color_bg = "23292b";
heyos_color_link = "FFFFFF";
heyos_color_text = "21b8ca";
heyos_color_url = "21b8ca";
</script>
<script type="text/javascript" src="http://admaster.heyos.com/core/bnr.js"></script></center><br />
Il metodo onScannedRobot() e&#8217; uno dei tanti metodi per gestire gli eventi forniti di default. Questo metodo viene automaticamente eseguito quando un robot nemico rientra nel campo visivo del nostro: in questo caso l&#8217;azione e&#8217; di sparare. L&#8217;oggetto e (sta per event) passato come argomento contiene informazioni utili come la direzione del proiettile, la distanza da noi, etc&#8230; non utilizzate nel nostro caso. Potete consultare la documentazione sulle API di Robocode che e&#8217; molto esaustiva a riguardo.</p>
<pre class="brush: java;">
	public void onHitByBullet(HitByBulletEvent e) {
		turnLeft(90 - e.getBearing());
	}
}
</pre>
<p style="text-align: justify;">L&#8217;ultimo metodo viene chiamato quando il nostro robot viene colpito da un proiettile: per evitare di essere colpiti di nuovo il robot si gira nella direzione perpendicolare al proiettile e quindi si sposta grazie alle istruzioni all&#8217;interno del metodo run(). Il metodo getBearing() restituisce l&#8217;angolo tra la direzione in cui ci stiamo muovendo e la direzione del proiettile ed e&#8217; sempre compreso tra -180 e 180, ed e&#8217; uno dei tanti metodi di cui vi parlavo in precedenza.</p>
<p style="text-align: justify;">Se provate a iniziare una battaglia tra questo robot e altri leggermente piu&#8217; avanzati presenti gia&#8217; di default, vi accorgerete che le sue prestazioni non sono proprio ottimali, di fatto perdera&#8217; la maggior parte delle battaglie se non tutte (ovviamente dipende contro chi lo fate combattere), ma e&#8217; comunque un inizio. Il metodo migliore per realizzare robot sempre piu&#8217; avanzati e&#8217; cominciare da subito a crearne: ricordate anche che avendo abbastanza padronanza con Java non ci sono limiti a quello che si puo&#8217; realizzare!</p>
]]></content:encoded>
			<wfw:commentRss>http://mindunpacked.com/2009/robocode-guerre-tra-robot-in-java/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Reti neurali attraverso algoritmi genetici in C++. Parte VI</title>
		<link>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-vi-ultima/</link>
		<comments>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-vi-ultima/#comments</comments>
		<pubDate>Tue, 30 Dec 2008 17:57:53 +0000</pubDate>
		<dc:creator>Francesco</dc:creator>
				<category><![CDATA[Informatica]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Intelligenza artificiale]]></category>
		<category><![CDATA[Reti neurali]]></category>

		<guid isPermaLink="false">http://mindunpacked.com/?p=345</guid>
		<description><![CDATA[Applicazioni.
Le reti neurali hanno diverse applicazioni e spaziano in diversi campi. In questo paragrafo elencherò alcune applicazioni interessanti che ho trovato sulla rete e poi mostrerò come è possibile creare un molto rudimentale sistema di riconoscimento dii  immagini, capace di riconoscere i numeri da 0 a 9.

Alcune applicazioni sono:
Vita artificiale: la vita artificiale tenta [...]]]></description>
			<content:encoded><![CDATA[<p align="center"><strong>Applicazioni.</strong></p>
<p align="justify">Le reti neurali hanno diverse applicazioni e spaziano in diversi campi. In questo paragrafo elencherò alcune applicazioni interessanti che ho trovato sulla rete e poi mostrerò come è possibile creare un molto rudimentale sistema di riconoscimento dii  immagini, capace di riconoscere i numeri da 0 a 9.</p>
<p><span id="more-345"></span></p>
<p align="justify">Alcune applicazioni sono:</p>
<p align="justify">Vita artificiale: la vita artificiale tenta di simulare l&#8217;evoluzione di organismi viventi tramite computer. Spesso gli organismi sono dotati di reti neurali per svolgere i loro compiti come trovare fonti di cibo, muoversi, etc&#8230;</p>
<p align="justify">Finanza: prevedere l&#8217;andamento dei mercati.</p>
<p align="justify">Riconoscimento di forme, immagini, facce, etc&#8230;</p>
<p align="justify">Quello che noi creeremo è un sistema molto rudimentale di riconoscimento delle immagini, di fatto potra riconoscere solo quelle immagini che gli abbiamo presentato nel training set e non sarà probabilmente in grado di riconoscerle se gli è stato modificato anche solamente qualche pixel. E&#8217; solo un esempio di base per mostrare cosa si può fare, ed anche in maniera abbastanza semplice. Le immagini che la nostra rete neurale sarà capace di riconoscere avranno dimensioni fisse (5&#215;7 pixels) e saranno simili a questa:</p>
<div id="attachment_346" class="wp-caption aligncenter" style="width: 108px"><a href="http://mindunpacked.com/wp-content/uploads/2008/12/1.png"><img class="size-medium wp-image-346" title="1" src="http://mindunpacked.com/wp-content/uploads/2008/12/1.png" alt="Immagine di esempio" width="98" height="138" /></a><p class="wp-caption-text">Immagine di esempio</p></div>
<p align="justify">
<p align="justify">
<p align="justify">
<p align="justify">I quadratini con il bordo nero sono semplicemente i pixel dell&#8217;immagine. Avendo questa immagine 7&#215;5 = 35 pixel in totale, la nostra rete neurale dovrà avere 35 neuroni di input, uno per ogni pixel. Per semplicità l&#8217;input potrà essere o 1 o 0, dove 1 simboleggia il colore rosso della precedente immagine e 0 lo sfondo. Così facendo si ottiene una matrice che rappresenta l&#8217;immagine, che nel caso di quella appena vista sarebbe questa:</p>
<p align="justify">
<p align="center">0 0 1 0 0</p>
<p align="center">0 1 1 0 0</p>
<p align="center">0 0 1 0 0</p>
<p align="center">0 0 1 0 0</p>
<p align="center">0 0 1 0 0</p>
<p align="center">0 0 1 0 0</p>
<p align="center">0 1 1 1 0</p>
<p align="center">
<p align="justify">Nell&#8217;allenamento della nostra rete, noi daremo un solo esempio per ogni numero, ma volendo potremmo dare più esempi diversi che rappresentano diversi modi di scrivere il numero 1 e così la nostra rete avrebbe una capacità più alta di riconoscere caratteri diversi (chiaramente l&#8217;allenamento sarà più lungo).</p>
<p align="justify">Per la nostra rete neurale creeremo noi il training set direttamente su un file di testo o altrimenti, essendo la rete neurale capace di riceve in input solo dei numeri, dovremmo creare un programma che data in input un&#8217;immagine crei la matrice numerica associata ad essa, ma siccome il nostro training set sarà molto piccolo le creeremo manualmente. Ogni riga dovrà contenere la rappresentazione di un numero, di conseguenza non avremo matrici nel file di input ma le righe saranno affiancate. Il file di input risulta così:</p>
<p align="justify">
<p style="text-align: center;">0 0 1 0 0 0 1 0 1 0 0 1 0 1 0 0 1 0 1 0 0 1 0 1 0 0 1 0 1 0 0 0 1 0 0</p>
<p style="text-align: center;">0 0 1 0 0 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 0</p>
<p style="text-align: center;">0 0 1 1 0 0 1 0 0 1 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 1 1 1 1</p>
<p style="text-align: center;">0 0 1 1 0 0 1 0 0 1 0 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 1 0 0 1 1 0</p>
<p style="text-align: center;">0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 0 0 0 1 0 0 0 0 1 0</p>
<p style="text-align: center;">0 1 1 1 0 0 1 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 0 0 1 0 0 0 0 1 0 1 1 1 0</p>
<p style="text-align: center;">0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 1 1 0 1 0 0 0 1 1 0 0 0 1 0 1 1 1 0</p>
<p style="text-align: center;">0 0 0 0 0 1 1 1 1 1 0 0 0 0 1 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 1 0 0 0 0</p>
<p style="text-align: center;">0 1 1 1 0 1 0 0 0 1 1 0 0 0 1 0 1 1 1 0 1 0 0 0 1 1 0 0 0 1 0 1 1 1 0</p>
<p style="text-align: center;">0 1 1 1 0 1 0 0 0 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0</p>
<p style="text-align: center;">
<p align="justify">Se spezzate ogni riga in parti composte da 5 elementi riottenete la matrice che rapresenta il numero.</p>
<p align="justify">Il file di output avrà tante righe quante quelle del file di input e sarà il seguente:</p>
<p align="justify">
<p style="text-align: center;">1 0 0 0 0 0 0 0 0 0</p>
<p style="text-align: center;">0 1 0 0 0 0 0 0 0 0</p>
<p style="text-align: center;">0 0 1 0 0 0 0 0 0 0</p>
<p style="text-align: center;">0 0 0 1 0 0 0 0 0 0</p>
<p style="text-align: center;">0 0 0 0 1 0 0 0 0 0</p>
<p style="text-align: center;">0 0 0 0 0 1 0 0 0 0</p>
<p style="text-align: center;">0 0 0 0 0 0 1 0 0 0</p>
<p style="text-align: center;">0 0 0 0 0 0 0 1 0 0</p>
<p style="text-align: center;">0 0 0 0 0 0 0 0 1 0</p>
<p style="text-align: center;">0 0 0 0 0 0 0 0 0 1</p>
<p><center><script type="text/javascript">
heyos_ad_user = 11334;
heyos_ad_type = "G";
heyos_ad_format = "1";
heyos_color_border = "23292b";
heyos_color_bg = "23292b";
heyos_color_link = "FFFFFF";
heyos_color_text = "21b8ca";
heyos_color_url = "21b8ca";
</script>
<script type="text/javascript" src="http://admaster.heyos.com/core/bnr.js"></script></center></p>
<p align="justify">I neuroni di output saranno 10 ed ognuno è associato ad un numero. Con il numero 0 il primo neurone emette un segnale mentre gli altri no, con il numero 1 il secondo neurone emette un segnale mentre gli altri no, etc&#8230; di conseguenza ci aspettiamo che dopo l&#8217;addestramento, quando passiamo in input, per esempio il numero 9 soltanto l&#8217;ultimo neurone si attiverà. Il codice è il seguente:</p>
<pre class="brush: cpp;">
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;sstream&gt;
#include &lt;vector&gt;
#include &lt;ctime&gt;
#include &lt;cmath&gt;

using namespace std;

#include &quot;Misc.hpp
#include &quot;GenAlg.hpp&quot;
#include &quot;NeuralNet.hpp&quot;
#include &quot;NNController.hpp&quot;

int main() {
NNController NC(35, 10, 1, 10);
NC.loadTrainingSet(&quot;input&quot;, &quot;output&quot;);
NC.train();
NC.saveWeights(&quot;img.txt&quot;);
cout &lt;&lt; &quot;*** La rete e' allenata ***&quot; &lt;&lt; endl;
}
</pre>
<p align="justify">Come vedete, abbiamo creato 35 neuroni di input, 10 di output ed un livello nacosto con 10 neuroni.</p>
<p align="center"><strong>LINKS</strong></p>
<p align="justify"><a href="http://www.ai-junkie.com/ann/evolved/nnt1.html">http://www.ai-junkie.com/ann/evolved/nnt1.html</a> &#8211; Questo tutorial in Inglese sulle reti neurali è uno dei migliori che abbia mai letto, tant&#8217;è che ne ho preso spunto per scrivere questo. Vengono usati anche lì gli algoritmi genetici per aggiornare la rete e troverete delle somiglianze anche nelle classi del codice (però vi assicuro che il codice per questo tutorial l&#8217;ho scritto da zero <img src='http://mindunpacked.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><a href="http://www.ai-junkie.com/ga/intro/gat1.html">http://www.ai-junkie.com/ga/intro/gat1.html</a> &#8211; Tutorial sugli algoritmi genetici, sempre in Inglese.</p>
<p><a href="http://blacklight.gotdns.org/wiki/index.php/Introduzione_ai_sistemi_fuzzy_e_alle_reti_neurali">http://blacklight.gotdns.org/wiki/index.php/Introduzione_ai_sistemi_fuzzy_e_alle_reti_neurali</a> &#8211; Implementazione di una rete neurale error back propagation in C, tutorial in Italiano.</p>
<p align="justify"><a href="http://www.aitalk.net/">http://www.aitalk.net</a> &#8211; Forum sull&#8217;intelligenza artificiale in lingua Inglese. Contiene anche una piccola sezione in lingua Italiana.</p>
<p>Ho anche realizzato la stessa guida in formato PDF: trovate <a href="http://www.mindunpacked.com/risorse/TutorialRetiNeurali.tar.gz">QUI</a> un archivio contenente tutti i file sorgente di cui abbiamo parlato e la guida in formato PDF (un po meglio impaginata e con qualche illustrazione in piu&#8217;).<br />
Alla prossi</p>
]]></content:encoded>
			<wfw:commentRss>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-vi-ultima/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Reti neurali attraverso algoritmi genetici in C++. Parte V</title>
		<link>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-v/</link>
		<comments>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-v/#comments</comments>
		<pubDate>Fri, 12 Dec 2008 13:07:07 +0000</pubDate>
		<dc:creator>Francesco</dc:creator>
				<category><![CDATA[Informatica]]></category>
		<category><![CDATA[Algoritmi genetici]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Intelligenza artificiale]]></category>
		<category><![CDATA[Reti neurali]]></category>

		<guid isPermaLink="false">http://mindunpacked.com/?p=252</guid>
		<description><![CDATA[In questa penultima parte della serie di articoli dedicati alle reti neurali, vedremo come creare una classe che riunisca tutto cio&#8217; che abbiamo programmato in precedenza e ne renda piu&#8217; facile l&#8217;utilizzo. Era gia&#8217; possibile utilizzare le classi cosi&#8217; com&#8217;erano, ma in questo modo ci rendiamo la vita piu&#8217; semplice. Inoltre c&#8217;e&#8217; da notare che [...]]]></description>
			<content:encoded><![CDATA[<p>In questa penultima parte della serie di articoli dedicati alle reti neurali, vedremo come creare una classe che riunisca tutto cio&#8217; che abbiamo programmato in precedenza e ne renda piu&#8217; facile l&#8217;utilizzo. Era gia&#8217; possibile utilizzare le classi cosi&#8217; com&#8217;erano, ma in questo modo ci rendiamo la vita piu&#8217; semplice. Inoltre c&#8217;e&#8217; da notare che quello che faremo oramai non ho molto a che fare con l&#8217;implementazione della rete, ma e&#8217; piu&#8217; una questione di ordine e OOP.</p>
<p><span id="more-252"></span></p>
<p align="center"><strong>Riunire il tutto.</strong></p>
<p align="justify">In questo piccolo paragrafo vedremo come fare comunicare tra loro le due classi attraverso una terza ed ultima classe che si occupa della gestione di tutte le operazioni. In realtà questa classe è superflua perchè è già possibile, con le sole classi Population e NeuralNet, creare una rete neurale funzionante ma essa ci rende molto più comoda la scritture del codice. Essa contiene per lo più funzioni ausiliarie come quella per caricare il training set (senza dover parsare manualmente il file), o per salvare e caricare i pesi della rete neurale e per questo non mi soffermerò molto sul codice. La funzione più importante è quella che esegue l&#8217;allenamento della rete neurale, ma vediamo prima il codice della classe:</p>
<pre class="brush: cpp;">
class NNController {
	public:
		NNController(int c_numInputNeurons, int c_numOutputNeurons,
		int c_numHiddenLayers, int c_numNeuronsPerLayer);
		void loadTrainingSet(string inputFile, string outputFile);
		void train();
		void saveWeights(string file);
		void loadWeights(string file);
		void test();
	private:
		vector&lt; vector &gt; inputData; // Training set
		vector&lt; vector &gt; outputData;
		NeuralNet* N;
		Population* P;
		Chromosome* C;
};
</pre>
<p align="justify">Il costruttore ha gli stessi parametri di quello della classe NeuralNet che servono ad inizializzare l&#8217;oggetto N all&#8217;interno di questa classe.</p>
<p align="justify">La funzione loadTrainingSet(string inputFile string outputFile) carica appunto il training set: essa riceve come argomenti i nomi di due file, il primo deve contenere gli input da passare alla rete neurale mentre il secondo gli output desiderati corrispondenti agli input. Per esempio, se stiamo addestrando la rete a fare la somma di due numeri, ed il file contenente l&#8217;input contenesse:</p>
<p align="justify">
<p align="justify">0 1</p>
<p align="justify">0 2</p>
<p align="justify">
<p align="justify">Quello contenente l&#8217;output dovrebbe contenere:</p>
<p align="justify">1</p>
<p align="justify">2</p>
<p align="justify">(Gli input devono essere separati tra di loro da uno spazio ed ogni riga deve contenere ovviamente sempre lo stesso numero di input che deve essere a sua volta uguale al numero dei neuroni di input specificato nel codice).</p>
<p align="justify">Le funzioni saveWeights(string file) e loadWeights(string file) servono rispettivamente a salvare i pesi della rete neurale (generalmente dopo che questa è allenata) e a poterli caricare in seguito senza dover ripetere l&#8217;addestramento.</p>
<p align="justify">La funzione test() serve invece a poter testare la rete con vari input dopo che questa è stata allenata. Ho conservato volutamente alla fine la funzione train() perchè è l&#8217;unica un po più complessa e serve ad effettuare l&#8217;allenamento della rete.</p>
<pre class="brush: cpp;">
void NNController::train() {
	double currentErr = 0; int i = 0; double bestFit = 99999999;&lt;
	while (bestFit &gt; MAX_ERROR) {
		if (i &gt;= MAX_EPOCHS) {
			break;
		}
		for (int j = 0; j &lt; POPULATION_SIZE; j++) {
			currentErr = 0;
			for (int k = 0; k &lt; inputData.size(); k++) {
				N-&gt;run(inputData[k], (*P)[j]);
				currentErr += N-&gt;globalError(outputData[k]) / inputData.size()
			}
			(*P)[j]-&gt;setFitness(currentErr);
		}
		bestFit = P-&gt;best()-&gt;getFitness();
		if (i % UPDATE == 0) {
			cout &lt;&lt; &quot;(&quot; &lt;&lt; i &lt;&lt; &quot;) - &quot;&lt;&lt; &quot;Best = &quot; &lt;&lt; bestFit
			     &lt;&lt; &quot;\t&quot; &lt;&lt; &quot;Worst = &quot; &lt;&lt; P-&gt;worst()-&gt;getFitness()
			     &lt;&lt; endl;
		}
		P-&gt;next();
		i++;
	}
}
</pre>
<p><!--adsense--></p>
<p align="justify">La funzione esegue un ciclo while che termina o quando il fitness raggiunge un livello accettabile (cioè l&#8217;errore raggiunto è minore o uguale all&#8217;errore massimo stabilito in precedenza e definito nella costante MAX_ERROR) oppure quando (vedete il break) il numero di epoche (cioè di cicli di riproduzione della popolazione) supera un certo limite. Dentro questo ciclo while c&#8217;è un ulteriore ciclo for che scorre tutti gli elementi della popolazione (che, come oramai dovrebbe essere chiaro, rappresenta una configurazione dei pesi della rete neurale) e per ognuno esegue tutto il training set in input. Il fitness di questo individuo sarà l&#8217;errore medio che il cromosoma ha sugli elementi del training set. Un errore pari a 0, che corrisponde alla perfezione assoluta, è praticamente impossibile da raggiungere e quindi, a seconda dei casi, ci si può accontentare di un errore più o meno alto come, per esempio, 0.001.</p>
<p align="justify">La funzione stampa anche un output ogni tanto per informare l&#8217;utente a che punto è arrivato l&#8217;allenamento, e dipende dal valore della costante UPDATE: se per esempio questa vale 50, ogni 50 generazioni sarà stampato un messaggio contente i fitness del migliore e del peggiore cromosoma della popolazione corrente.</p>
<p align="justify">Quando questa funzione termina la nostra rete neurale è allenata (con un errore massimo stabilito dalla costante MAX_ERROR) ed è possibile testarla tramite il metodo test() fornito dalla stessa classe.</p>
<p align="justify">Vediamo ora un codice di esempio di rete neurale realizzata con le nostre classi:</p>
<pre class="brush: cpp;">
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;sstream&gt;
#include &lt;vector&gt;
#include &lt;ctime&gt;
#include &lt;cmath&gt;

using namespace std;

/* Misc.hpp è un header che contiene le varie costanti come
MAX_POPULATION, ELITISM, MAX_TOUR, etc...
Gli altri header contengono le varie classi */
#include &quot;Misc.hpp&quot;
#include &quot;GenAlg.hpp&quot;
#include &quot;NeuralNet.hpp&quot;
#include &quot;NNController.hpp&quot;

int main() {
	/* Alla riga seguente creiamo una rete neurale con 2 neuroni
	di input, 1 neurone di output e 0 layer nascosti */
	NNController NC(2, 1, 0, 0);
	/* Carica il training set contenuto nei due file */
	NC.loadTrainingSet(&quot;input&quot;, &quot;output&quot;);
	NC.train();
	cout &lt;&lt; &quot;*** La rete e' allenata ***&quot; &lt;&lt; endl;
	NC.test();
}
</pre>
<p align="justify">Come vedete il codice è davvero basilare eppure una rete neurale come quella di questo esempio, sarebbe in grado di imparare a fare la somma di due numeri con soli tre neuroni!</p>
<p align="center"><strong>Come impostare una rete neurale.</strong></p>
<p align="justify">Abbiamo visto la creazione pratica di una rete neurale ma come avete notato il risultato ottenuto da una rete dipende da una miriade di fattori come per esempio il numero di livelli, il numero di neuroni presente nei livelli nascosti, la funzione di trasferimento, etc&#8230; di conseguenza vi starete probabilmente chiedendo quali sono le impostazioni migliori per una rete neurale. Chiaramente non esistono delle impostazioni migliori in assoluto, ma dipende tutto dal problema che dobbiamo analizzare, di conseguenza quelle che scriverò qui saranno solo delle brevi linee guida da tenere in considerazione ma non delle regole fisse.</p>
<p align="justify">Ho gia parlato in precedenza delle funzioni di attivazione e di quali sia più comodo usare a seconda dei casi, ora parleremo del numero dei livelli.</p>
<p align="justify">Quanti livelli nascosti vanno usati? Nell&#8217;esempio precedente abbiamo creato una rete capace di imparare a fare la somma dei due numeri senza utilizzare nessun livello nascosto e in molti casi può verificarsi questa eventualità. Generalmente, nei casi restanti, un solo livello nascosto è più che sufficiente per ottenere risultati corretti. Dal punto di vista matematico possiamo dire che una rete neurale con i soli livelli si input e outputè capace di risolvere problemi linearmente separabili, mentre per problemi non linearmente separabili sono richiesti uno o più livelli nascosti.</p>
<p align="justify">Il numero di neuroni per ogni livello nascosto può essere un&#8217;altra incognita nella creazione della rete neurale: esso dipende da più fattori come il numero dei neuroni di input e di output, la dimensione del training set, la funzione di attivazione, l&#8217;algoritmo di apprendimento, etc&#8230; Esistono diverse regole approssimative che cercano di fornire un modo per calcolare il numero di unità nascoste necessarie (per esempio: &#8220;il numero di unità nascoste deve essere sempre minore del doppio dei neuroni di input&#8221;) ma molto spesso esse sono inutili. La cosa migliore è fare alcune prove e trovare il numero di unità che fornisce un errore minimo.</p>
]]></content:encoded>
			<wfw:commentRss>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-v/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Reti neurali attraverso algoritmi genetici in C++. Parte IV</title>
		<link>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-iv/</link>
		<comments>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-iv/#comments</comments>
		<pubDate>Tue, 09 Dec 2008 12:04:57 +0000</pubDate>
		<dc:creator>Francesco</dc:creator>
				<category><![CDATA[Informatica]]></category>
		<category><![CDATA[Algoritmi genetici]]></category>
		<category><![CDATA[Intelligenza artificiale]]></category>
		<category><![CDATA[Reti neurali]]></category>

		<guid isPermaLink="false">http://mindunpacked.com/?p=200</guid>
		<description><![CDATA[Le classi Chromosome e Population.
Abbiamo finito l&#8217;implementazione della rete ed ora possiamo passare alla parte relativa all&#8217;addestramento della stessa, che come ho già detto, verrà realizzata tramite algoritmi genetici. Vediamo la classe Chromosome:


class Chromosome {
	public:
		Chromosome(int c_w);
		Chromosome(vector&#60;double&#62; weights); // overload
		void setWeight(int i, double val);
		double getWeight(int i) const;
		void setFitness(double fit);
		double getFitness() const;
		int getWeightsNum() const;
		void mutate();
	private:
		vector&#60;double&#62; DNA;
		double fitness;
};

Innanzitutto [...]]]></description>
			<content:encoded><![CDATA[<p align="center"><strong>Le classi Chromosome e Population.</strong></p>
<p align="justify">Abbiamo finito l&#8217;implementazione della rete ed ora possiamo passare alla parte relativa all&#8217;addestramento della stessa, che come ho già detto, verrà realizzata tramite algoritmi genetici. Vediamo la classe Chromosome:</p>
<p align="justify"><span id="more-200"></span></p>
<pre class="brush: cpp;">
class Chromosome {
	public:
		Chromosome(int c_w);
		Chromosome(vector&lt;double&gt; weights); // overload
		void setWeight(int i, double val);
		double getWeight(int i) const;
		void setFitness(double fit);
		double getFitness() const;
		int getWeightsNum() const;
		void mutate();
	private:
		vector&lt;double&gt; DNA;
		double fitness;
};
</pre>
<p align="justify">Innanzitutto essa possiede due costruttori. Il primo serve nel caso volessimo inizializzare un cromosoma con pesi random specificando solamente il numero dei pesi  (in pratica la lunghezza del DNA); il secondo serve quando vogliamo creare un cromosoma con dei pesi ben precisi passandogeli come argomento: quest&#8217;ultimo viene usato per implementare una funzione che permette di salvare i pesi dopo che la rete neurale è allenata e di poterli caricare per evitare di dover ripetere ogni volta l&#8217;allenamento.</p>
<p align="justify">Come potete vedere le funzioni getWieght(int) e setWeight(int, double) hanno dei compiti abbastanza evidenti così come setFitness(double) e getFitness().</p>
<p align="justify">La funzione mutate() applica il famoso operatore genetico delle mutazione al cromosoma, vediamone il codice:</p>
<pre class="brush: cpp;">
void Chromosome::mutate() {
	if (dRand() &lt;= MUTATION_RATE) {
		DNA[rand()%DNA.size()] += wRand()*MAX_PERTURBATION;
	}
}</pre>
<p align="justify">Viene generato un numero random compreso tra 0 e 1 e se è minore del valore di MUTATION_RATE la mutazione viene effettuata. Nella parte sugli algoritmi genetici avevamo visto che, quando il DNA è codificato in forma binaria la mutazione agisce semplicemente trasformando uno 0 in un 1 o viceversa. Nel nostro caso, invece, la mutazione non fa altro che aggiungere o sottrarre un piccolo valore compreso tra 0 e MAX_PERTURBATION ad un peso qualsiasi del DNA (wRand() genera infatti un numero compreso tra -1 e 1 quindi il valore di MAX_PERTURBATION può essere sottratto o aggiunto, a seconda del numero generato da wRand()).</p>
<p align="justify">La classe Population ingloba un array di oggetti Chromosome* cioè la popolazione vera e propria, più una serie di metodi come la riproduzione, il crossover, etc&#8230; ecco il codice:</p>
<pre class="brush: cpp;">
class Population {
	public:
		Population(int c_num, int c_numw);
		void crossover(Chromosome* a1, Chromosome* a2, Chromosome* b1, Chromosome* b2);
		Chromosome* getChromosomeFromTournament();
		void next();

		Chromosome* best();
		Chromosome* worst();
		Chromosome* operator[](int i);
	private:
		int length;
		vector&lt;Chromosome*&gt; popv;
};</pre>
<p><!--adsense--><br />
Il costruttore è abbastanza semplice: prende come argomenti il numero di individui di cui è formata la popolazione, ed il numero di pesi per ogni individui cioè la lunghezza del DNA (che chiaramente è uguale per tutti gli individui).</p>
<p align="justify">Il metodo del crossover incrocia il DNA dei primi due cromosomi che gli passiamo come argomento e lo mette negli ultimi due, sempre se il numero random generato sia minore della costante CROSSOVER_RATE: un valore di quest&#8217;ultima costante pari a 0.7, per esempio, vuol dire che nel 70% dei casi il crossover viene effettuato. Se ciò non avviene gli individui si riproducono così come sono.</p>
<pre class="brush: cpp;">
void Population::crossover(Chromosome* a1, Chromosome* a2, Chromosome* b1, Chromosome* b2) {
	if (dRand() &lt;= CROSSOVER_RATE) {
		int crosspoint = rand()%length;

		for (int i = 0; i &lt; crosspoint; i++) {
			b1-&gt;setWeight(i, a1-&gt;getWeight(i));
			b2-&gt;setWeight(i, a2-&gt;getWeight(i));

			b1-&gt;setWeight(length-(i+1),
			a1-&gt;getWeight(length-(i+1)));
			b2-&gt;setWeight(length-(i+1),
			a2-&gt;getWeight(length-(i+1)));
		}
	} else { // Se non fa il crossover riproduce gli stessi cromosomi.
		b1 = a1;
		b2 = a2;
	}
}</pre>
<p>La funzione getChromosomeFromTournament() svolge il cosiddetto &#8220;torneo&#8221; fra i cromosomi e seleziona quello con il miglior fitness. Essa non è altro che il nostro metodo di selezione:</p>
<pre class="brush: cpp;">
Chromosome* Population::getChromosomeFromTournament() {
	vector&lt;Chromosome*&gt; r;
	for (int i = 0; i &lt; NUM_TOUR; i++) {
		r.push_back(popv[rand()%popv.size()]);
	}
	sort(r.begin(), r.end(), compareChromosomes);
	return r[0];
}
</pre>
<p><!--adsense--></p>
<p align="justify">Per svolgere il &#8220;torneo&#8221; viene creato un vettore che conterrà i cromosomi scelti casualmente dalla popolazione: NUM_TOUR è un&#8217;altra costante che definisce quanti cromosomi saranno scelti. Più NUM_TOUR è alta più basse saranno le probabilità degli individui più deboli di sopravvivere e ricordate che questo può non essere sempre un vantaggio; nel caso estremo in cui NUM_TOUR sia uguale alla grandezza della popolazione verrebbe sempre e solo scelto il cromosoma migliore della prima generazione (che poi tanto buono non è visto che nella prima generazione il DNA è random) portando così a tutt&#8217;altro che la soluzione del problema.</p>
<p align="justify">L&#8217;array così ottenuto viene ordinato in base al fitness (la funzione compareChromosomes() non fa altro che restituire il cromosoma che ha il fitness minore tra i due che riceve come argomento) e così l&#8217;elemento 0 dell&#8217;array conterrà quello con il fitness più basso (ricordate che per noi fitness più basso equivale ad errore più basso e quindi a punteggio migliore) mentre l&#8217;ultimo quello con fitness più alto. Questa funzione, insieme a quella del crossover, viene utilizzata nel metodo next() che effettua la riproduzione dell&#8217;intera popolazione finchè non si ha una nuova popolazione con lo stesso numero di individui della precedente.</p>
<pre class="brush: cpp;">
void Population::next() {
	vector&lt;Chromosome*&gt; newPop;

	sort(popv.begin(), popv.end(), compareChromosomes);

	Chromosome* b1 = new Chromosome(length);
	Chromosome* b2 = new Chromosome(length);

	// Garantisce la sopravvivenza dell'inviduo con il fitness piu' alto
	// della popolazione precedente.
	for (int i = 0; i &lt; ELITISM; i++) {
		newPop.push_back(popv[0]);
	}
	while (newPop.size() &lt; popv.size()) {
		Chromosome* a1 = getChromosomeFromTournament();
		Chromosome* a2 = getChromosomeFromTournament();

		crossover(a1, a2, b1, b2);
		b1-&gt;mutate();
		b2-&gt;mutate();
		newPop.push_back(b1); newPop.push_back(b2);
	}

	popv = newPop;
}</pre>
<p align="justify">Viene creato inizialmente un nuovo vettore che conterrà i nuovi individui ed il vettore contenente la popolazione viene ordinato in ordine di fitness (come abbiamo fatto nel metodo per svolgere il &#8220;torneo&#8221;): questo serve per mettere in atto un procedimento particolare definito elitismo. L&#8217;elisitmo consiste nel fare sopravvivere sempre, nella generazione successiva, una o più copie dell&#8217;elemento che nella generazione precedente aveva totalizzato il punteggio migliore: questo ci garantisce che, quali che siano le ricombinazioni che vengono effettuate sui nuovi individui, non perderemo mai il risultato raggiunto, e inoltre,  queste copie possono fornire un aiuto decisivo al raggiungimento della soluzione. ELITISM è una costante che definisce quante copie del miglior cromosma vadano inserite nella nuova popolazione. Nel ciclo while vengono selezionati due cromosomi dal &#8220;torneo&#8221;, viene fatto su di loro il crossover e sui nuovi individui generati viene effettuata una mutazione (sempre con la percentuale che abbiamo stabilito nella costante MUTATION_RATE) e poi vengono inseriti nel nuovo vettore. Quando questo vettore raggiunge le dimensioni della vecchia popolazione, si esce dal ciclo ed esso viene copiato sulla popolazione originale.</p>
<p align="justify">Le altre funzioni della classe sono abbastanza ovvie: best() serve a selezionare il cromosoma con il fitness migliore, worst() il peggiore e l&#8217;overloading dell&#8217;operatore [] ci permette di accedere ai membri della popolazione come se fossero elementi di un array senza dover scrivere un apposita funzione del tipo getChromosome(int i) (come quella che abbiamo usato per i neuroni: volendo anche lì si può fare infatti l&#8217;overloading dell&#8217;operatore []).</p>
<p align="justify">Manca solo una classe per riunire tutto il codice che abbiamo fatto fino ad ora e la nostra rete sarà utilizzabile (anche se, in pratica, e&#8217; utilizzabile anche da adesso con qualche sforzo in più): la vedremo nella prossima parte, intanto vi do i link a tutte le parti precedenti:</p>
<ol>
<li><a title="Reti neurali e algoritmi genetici parte I" href="http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-i/" target="_self">Reti neurali e algoritmi genetici I</a></li>
<li><a title="Reti neurali e algoritmi genetici parte II" href="http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-ii/" target="_self">Reti neurali e algoritmi genetici II</a></li>
<li><a title="Reti neurali e algoritmi genetici parte III" href="http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-iii/" target="_self">Reti neurali e algoritmi genetici III</a></li>
</ol>
<p>Alla prossima&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-iv/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Reti neurali attraverso algoritmi genetici in C++. Parte III</title>
		<link>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-iii/</link>
		<comments>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-iii/#comments</comments>
		<pubDate>Fri, 28 Nov 2008 22:14:33 +0000</pubDate>
		<dc:creator>Francesco</dc:creator>
				<category><![CDATA[Informatica]]></category>
		<category><![CDATA[Algoritmi genetici]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Intelligenza artificiale]]></category>
		<category><![CDATA[Reti neurali]]></category>

		<guid isPermaLink="false">http://mindunpacked.com/?p=132</guid>
		<description><![CDATA[IMPLEMENTAZIONE
Ok, siamo arrivati alla parte più tecnica cioè all&#8217;implementazione vera e propria della rete. Ho scelto di utilizzare il C++ e di programmare tutto ad oggetti perchè, anche se questo approccio sacrifica forse un po&#8217; la velocità, rende, almeno per me, il tutto più comprensibile e riutilizzabile (dalle classi che qui espongo ho costruito poi [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: center;"><strong>IMPLEMENTAZIONE</strong></p>
<p align="justify">Ok, siamo arrivati alla parte più tecnica cioè all&#8217;implementazione vera e propria della rete. Ho scelto di utilizzare il C++ e di programmare tutto ad oggetti perchè, anche se questo approccio sacrifica forse un po&#8217; la velocità, rende, almeno per me, il tutto più comprensibile e riutilizzabile (dalle classi che qui espongo ho costruito poi una libreria per l&#8217;utilizzo delle reti neurali).</p>
<p><span id="more-132"></span></p>
<p align="justify">Cominciamo a partire dalle classi della rete neurale.</p>
<p align="center"><strong>Le classi Neuron, NeuronLayer e NeuralNet </strong></p>
<p align="justify">La prima classe che implementeremo è la classe <strong>Neuron</strong>, che rappresenta la struttura più piccola della rete neurale: il neurone.</p>
<p align="justify">Ogni neurone come abbiamo visto ha dei collegamenti pesati in entrata (tranne i neuroni di input) e un valore, che è contiene il valore del neurone.</p>
<p align="justify">Il codice della classe Neuron è il seguente:</p>
<pre class="brush: cpp;">
class Neuron {
	public:
		Neuron(int c_numWeights);
		void setValue(double v);
		double getValue() const;
		void setWeight(int i, double v);
		double getWeight(int i) const;
		int getWeightsNum(bool bias) const;
	private:
		double value;
		vector&lt;double&gt; weights;
};
</pre>
<p>I metodi di questa classe sono tutti molto facili da capire: riporto qui solamente il codice del costrutture e di getWeightsNum(bool):</p>
<pre class="brush: cpp;">
// Costruttore
Neuron::Neuron(int c_numWeights) {
	for (int i = 0; i &lt; c_numWeights+1; i++) {
		weights.push_back(wRand());
	}
}
int Neuron::getWeightsNum(bool bias) const {
	return (bias) ? weights.size() : weights.size() 1;
}
</pre>
<p align="justify">Il costruttore riceve come argomento il numero dei pesi in entrata per questo neurone e li inizializza a valori random (wRand() è una funzione definita in un file header che restituisce un numero compreso tra -1 e 1). Se osservate bene il ciclo va da 0 a c_numWeights+1 questo perchè viene creato un peso aggiuntivo chiamato bias, la cui funzione vi sarà chiara fra poco.</p>
<p align="justify">Nella classe Neuron avremmo potuto anche creare un puntatore a funzione che puntasse alla funzione di attivazione (o funzione di trasferimento) del neurone ed in questa maniera avremmo potuto impostare una funzione di attivazione diversa per ogni neurone (o per ogni livello), ma visto che la nostra funzione sarà uguale per tutti i neuroni, ho preferito non farlo per questioni di semplicità.</p>
<p align="justify">E&#8217; arrivato il momento di spendere due parole proprio sulle funzioni di attivazione. La <strong>funzione di attivazione</strong> può essere una qualunque funzione, come per esempio , ma alcune offrono risultati migliori in base al contesto nella quale sono utilizzate. Scegliere la funzione giusta può essere determinante per ottenere buoni risultati.</p>
<p align="justify">Vediamo alcune funzioni comunemente usate:</p>
<p align="justify">
<p align="justify"><strong>Funzione a gradino: y = 1 per x &gt;= 0, 0 per x &lt; 0</strong></p>
<p align="justify">La funzione a gradino (chiamata così per il suo grafico) è la funzione che più rispetta il comportamento del <strong>neurone biologico</strong>. Infatti essa restituisce 1, cioè propaga il segnale, se la somma degli input ricevuti e maggiore o uguale a un certo numero , che altro non è che la soglia di attivazione. E&#8217; utile utilizzare questa funzione quando vogliamo che la nostra rete abbia un risultato che o 1 o 0 e che quindi separi gli input che gli forniamo in due classi distinte.</p>
<p align="justify">
<p align="justify"><span style="color: #808080;"><strong>Funzione identità: y = x</strong></span></p>
<p align="justify">Non penso ci sia molto da dire su questa funzione, l&#8217;unica cosa da sapere è che si utilizza quando il nostro output non è limitato e può variare da meno infinito a più infinito.</p>
<p align="justify">
<p align="justify"><strong>Funzione sigmoidale: y = 1/(1+e^(-x)) </strong></p>
<p align="justify">
<p align="justify">La funzione sigmoidale è una delle funzioni più utilizzate. Essa passa sempre per il punto (0, 1/2) ed è compresa tra 0 e 1. Proprio per quest&#8217;ultimo fatto non può essere utilizzata se ci aspettiamo come output numeri numeri che non sono compresi in questo intervallo, per i quali è necessario utilizzare altre funzioni.</p>
<p align="justify">
<p align="justify">
<p align="justify"><strong>Tangente iperbolica: y = tanh(x)</strong></p>
<p align="justify">La tangente iperbolica è molto simile alla funzione sigmoidale solo che il suo output è compreso tra -1 e 1 e passa sempre per l&#8217;origine. Come al solito va utilizzata solo nei casi in cui l&#8217;output deve essere compreso tra questi valori e per esempio non andrebbe bene se volessimo allenare la rete a fare la somma di due numeri qualsiasi. In realtà le cose non stanno esattamente così, perchè, come vedremo in seguito, esiste un apposito valore (bias) per ogni neurone che serve a spostare il centro della funzione e quindi ad ottenere valori che non sono più compresi tra -1 e 1 ma tra meno infinito  e  più infinito.</p>
<p align="justify">In ogni caso è comunque preferibile una funzione di trasferimento lineare nei casi in cui il nostro output possa variare in maniera illimitata.</p>
<p align="justify">
<p><!--adsense--></p>
<p align="justify">Dopo questa parentesi a proposito delle <strong>funzioni di attivazion</strong>e torniamo alla nostra implementazione. Una volta che il neurone fa la sommatoria pesata di tutti gli input ricevuti passa questo valore alla funzione di attivazione che, a sua volta, restituisce un altro valore. Quest&#8217;ultimo sarà propagato ai neuroni successivi e così via.</p>
<p align="justify">La seconda classe di cui ci occuperemo è NeuronLayer, che rappresenta uno strato di neuroni:</p>
<p align="justify">
<pre class="brush: cpp;">
class NeuronLayer {
	public:
		NeuronLayer(int c_numNeurons, int c_weightsPerNeuron);
		Neuron* getNeuron(int i) const;
		int getNeuronsNum() const;
	private:
		vector&lt;Neuron*&gt; neurons;
};
</pre>
<p align="justify">
<p align="justify">Come è logico, la classe <strong>NeuronLayer</strong> dovrà contenere un insieme di neuroni e per questo usiamo la classe vector della STL per istanziare un vettore di oggetti Neuron*.</p>
<p align="justify">Anche qui i metodi sono abbastanza semplici: il costruttore riceve come parametri il numero dei neuroni facenti parte del layer ed il numero di collegamenti in entrata (e quindi di pesi) per ogni neurone. La funzione getNeuron(int) riceve come parametro un intero i e restituisce l&#8217;i-esimo neurone del livello; l&#8217;ultima funzione restituisce il numero totale dei neuroni facenti parti del livello.</p>
<p align="justify">
<p align="justify">Siamo giunti alla parte cruciale del nostro programma e cioè la classe NeuralNet.</p>
<p align="justify">Sarà lei che si occuperà di collegare i neuroni fra di loro, di propagare i segnali attraverso i neuroni e di darci l&#8217;output restituito dalla rete.</p>
<p align="justify">
<pre class="brush: cpp;">
class NeuralNet {
	public:
		NeuralNet(int c_numInputNeurons, int c_numOutputNeurons,
		int c_numHiddenLayers, int c_numNeuronsPerLayer);
		void createNet();
		void run(vector&lt;double&gt; input, Chromosome* d);
		int totalNumWeights(bool bias);
		void dump();
		double globalError(vector&lt;double&gt; des);
		void printOutput();
	private:
		vector&lt;NeuronLayer*&gt; layers;
		int numInputNeurons,
		numOutputNeurons,
		numHiddenLayers,
		numNeuronsPerLayer;
}
</pre>
<p align="justify">
<p align="justify">Il costruttore della classe <strong>NeuralNet</strong> riceve in input 4 parametri che sono rispettivamente il numero dei neuroni di input, il numero dei neuroni di output, il numero dei livelli nascosti e il numero di neuroni contenuti nei livelli nascosti.</p>
<p align="justify">Il costruttore non fa altro che prendere questi argomenti e salvarli nelle quattro variabili apposite. Il vettore layers contiene dei puntatori ad oggetti NeuronLayer che saranno appunto i vari livelli della rete neurale. Due sono i metodi più importanti di questa classe: createNet() e run(vector&lt;double&gt;, Chromosome*). Il primo inizializza l&#8217;array layers creando i livelli necessari ed i collegamenti tra i vari neuroni, mentre il secondo, esegue la rete neurale passandogli in input i valori contenuti nell&#8217;array input ed utilizzando come pesi della rete il DNA dell&#8217;oggeto Chromosome*. Vediamo il codice di queste due funzioni:</p>
<pre class="brush: cpp;">
void NeuralNet::createNet() {
	if (numHiddenLayers &gt; 0) {
		// Se ci sono layer nascosti li crea
		NeuronLayer* HL = new NeuronLayer(numNeuronsPerLayer,
							numInputNeurons);
		layers.push_back(HL);
		for (int i = 1; i &lt; numHiddenLayers; i++) {
			NeuronLayer* HL = new NeuronLayer(numNeuronsPerLayer,
								numNeuronsPerLayer);
			layers.push_back(HL);
		}
		NeuronLayer* OL = new NeuronLayer(numOutputNeurons,
							numNeuronsPerLayer);
		layers.push_back(OL);
	} else {
		NeuronLayer* OL = new NeuronLayer(numOutputNeurons,
							numInputNeurons);
		layers.push_back(OL);
	}
}
</pre>
<p align="justify">La prima cosa che la funzione fa è quella di verificare se deve creare dei <strong>layer nascosti</strong> o meno. Nel primo caso crea il primo layer nacosto in cui ogni neurone ha un numero di pesi pari al numero dei neuroni in input (infatti i neuroni del primo layer ricevono i dati proprio dai neuroni di input)  e crea i layer seguenti in cui ogni neurone ha un numero di pesi pari al numero di neuroni presenti negli strati precedenti a lui, che essendo tutti strati nascosti avranno un numero di neuroni pari al contenuto della variabile numNeuronsPerLayer; come ultima cosa viene creato il layer di output. Se non ci sono layer nascosto la prima parte viene saltata e viene creato un layer di output collegato direttamente a quello di input.</p>
<p><!--adsense--></p>
<p align="justify">La funzione run() è un po più complessa. Ho evitato di scriverla tutta qui per problemi di impagnazione, quindi vi conviene leggerla direttamente dal file sorgente. Comunque, la prima cosa che la funziona fa è controllare che il numero degli input ricevuti dalla funzione corrisponda effettivamente al numero di neuroni di input: se così non è l&#8217;esecuzione del programma termina dopo aver stampato un messaggio di errore.</p>
<p align="justify">Successivamente la funzione deve impostare i pesi della rete neurale con il DNA del <strong>cromosoma</strong> che gli passiamo come argomento, per fare questo si utilizzano tre cicli for annidati: il primo scorre tutti i livelli della rete neurale, il secondo tutti i neuroni di ogni livello ed il terzo tutti i pesi di ogni neurone, sosituendoli con quelli presenti nel cromosoma.</p>
<p align="justify">Dopo aver impostato i pesi non ci resta altro che dare alla rete l&#8217;input e propagarlo attraverso i neuroni. Si usano anche qui tre cicli for annidati per scorrere tutti i neuroni della rete neurale e per calcolare la somma degli input pesati. Notate che c&#8217;è un if che distingue due casi: se ci troviamo al primo livello nascosto l&#8217;input dovremo prenderlo dai neuroni di input (che altro non sono che gli elementi dell&#8217;array passato come argomento): questo lo facciamo alla linea:</p>
<p align="justify">
<pre class="brush: cpp;">
totInput += layers[i]-&gt;getNeuron(j)-&gt;getWeight(k) * input[k];
</pre>
<p align="justify">
<p align="justify">Come vedete, il k-esimo peso del neurone viene moltiplicato per il k-esimo input, che altro non è che un valore dall&#8217;array.</p>
<p align="justify">Quando non siamo più al primo livello, ma a quelli successivi l&#8217;input andrà preso da neuroni precedenti ed infatti:</p>
<p align="justify">
<pre class="brush: cpp;">
totInput += layers[i]-&gt;getNeuron(j)-&gt;getWeight(k) * layers[i-1]-&gt;getNeuron(k)&gt;getValue();
</pre>
<p align="left">
<p align="justify">Il k-esimo peso del neurone viene moltiplicato per il valore contenuto nel k-esimo neurone del livello precedente (layers[i-1]). La backslash dopo il * sta solamente ad indicare che la linea è spezzata per ragioni di formattazione, ma che il codice va su una sola riga. La variabile totInput conterrà alla fine del ciclo la sommatoria pesata degli input del neurone. Subito dopo il ciclo viene definita una variabile con il valore dell&#8217;ultimo peso del neurone (questo peso non è stato preso in considerazione quando facevamo la sommatoria, se notate, nel ciclo abbiamo passato il parametro false alla funzione getWeightsNum() facendoci così restituire il numero dei pesi meno 1). Questo sarà il bias che alla riga successiva viene sottratto al valore totale del  neurone dopo che quest&#8217;ultimo è stato passato attraverso la funzione sigmoid() cioè la funzione di trasferimento sigmoidale. Dal punto di vista grafico potete immaginare il bias semplicemente come una traslazione sull&#8217;asse y del grafico della funzione e questo comporta che la funzione non sia centrata nel punto per ogni neurone ma che ogni neurone abbia appunto una funzione centrata in un punto differente.</p>
<p align="justify">Il compito della funzione è finito qui, poiché dopo tutti questi cicli i neuroni dell&#8217;ultimo livello, cioè i neuroni di output conterrano il risultato ottenuto dalla rete.</p>
<p align="justify">L&#8217;ultima funzione degna di nota è globalError(): essa calcola l&#8217;errore della rete facendo la differenza tra ogni output ed il rispettivo output desiderato specificato nel training set. Ci servirà in seguito per assegnare il fitness ad i nostri cromosomi.</p>
<p align="justify">Nella prossima parte parleremo delle classi relative alla gestione dei cromosomi e metteremo in pratica quello che abbiamo visto teoricamente nella parte seconda quindi continuate a visitarci!</p>
<p align="justify">Link alle parti precedenti:</p>
<p align="justify"><a title="Reti neurali attraverso algoritmi genetici in C++. Parte I" href="http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-i/" target="_self">Parte I</a></p>
<p align="justify"><a title="Reti neurali attraverso algoritmi genetici in C++. Parte II" href="http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-ii/" target="_self">Parte II</a></p>
]]></content:encoded>
			<wfw:commentRss>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-iii/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Reti neurali attraverso algoritmi genetici in C++. Parte I</title>
		<link>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-i/</link>
		<comments>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-i/#comments</comments>
		<pubDate>Wed, 19 Nov 2008 23:05:10 +0000</pubDate>
		<dc:creator>Francesco</dc:creator>
				<category><![CDATA[Informatica]]></category>
		<category><![CDATA[Algoritmi genetici]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Intelligenza artificiale]]></category>
		<category><![CDATA[Reti neurali]]></category>

		<guid isPermaLink="false">http://mindunpacked.com/?p=37</guid>
		<description><![CDATA[PREFAZIONE
Le reti neurali sono un argomento molto ostico per molti all&#8217;inizio.
Anche io per riuscire ad implementare la mia prima rete neurale funzionante ci ho impiegato molto tempo ma ho soprattutto dovuto provare e riprovare più volte passando per diversi fallimenti. Mi sono accorto che molti dei tutorial che si trovano in giro tralasciano alcuni aspetti [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: center"><strong>PREFAZIONE</strong></p>
<p style="text-align: justify;">Le <strong>reti neurali</strong> sono un argomento molto ostico per molti all&#8217;inizio.<br />
Anche io per riuscire ad implementare la mia prima <strong>rete neurale</strong> funzionante ci ho impiegato molto tempo ma ho soprattutto dovuto provare e riprovare più volte passando per diversi fallimenti. Mi sono accorto che molti dei tutorial che si trovano in giro tralasciano alcuni aspetti o li spiegano in maniera poco chiara, e tra l&#8217;altro i tutorial in lingua italiana sull&#8217;argomento sono anche pochi.<br />
Con questo tutorial cercherò di creare un documento che comprenda almeno le basi per rendere chiunque lo legga, ed abbia alcuni prerequisiti elencati in seguito, capace di implementare una rete neurale.<br />
<span id="more-37"></span></p>
<p style="text-align: center"><strong>Nota sul codice.</strong></p>
<p style="text-align: justify;">Prima di iniziare va scritta una piccola nota sul codice: il codice che troverete nell&#8217;archivio che vi sarà dato alla fine di questa guida, non è esattamente lo stesso che trovate scritto in questa serie di tutorial. Questo perchè ho fatto in seguito delle modifiche (minori) che comunque non cambiano in sostanza la struttura del codice, ma aggiungono solo qualche funzionalità alla rete neurale. Non dovrebbe essere difficile seguirlo lo stesso.</p>
<p style="text-align: center"><strong>PREREQUISITI</strong></p>
<p style="text-align: justify;">Ho cercato di rendere questa guida comprensibile alla maggior parte delle persone (anche a quelle che sull&#8217;argomento reti neurali sono poco informate) ma restano sempre degli argomenti che vengono dati per scontati per la comprensione del testo.<br />
Sono:</p>
<ul>
<li>Buona conoscenza del C++ e della programmazione OO</li>
<li>Basi matematiche</li>
<li>Conoscenza degli <strong>algoritmi genetici</strong></li>
</ul>
<p style="text-align: justify;">L&#8217;ultimo punto non è strettamente necessario, visto che tenterò di spiegare almeno quello che serve sapere sugli algoritmi genetici per implementare la nostra rete, ma se avete gia&#8217; una conoscenza dell&#8217;argomento di certo avrete meno difficoltà a comprendere alcuni concetti. Alla fine del tutorial sono consigliati una serie di link utili la cui lettura e caldamente consigliata.</p>
<p style="text-align: center"><strong>PARTE GENERALE</strong></p>
<p style="text-align: center"><strong>Introduzione.</strong></p>
<p style="text-align: justify;">Questa Parte generale del tutorial contiene alcuni elementi di base come il funzionamento di un <strong>neurone</strong> biologico, la struttura di un neurone artificiale (cioè quello che noi implementeremo) e la struttura di una rete neurale artificiale. Ho inserito anche una piccola parte sugli <strong>algoritmi genetici</strong> che non tratta esaustivamente l&#8217;argomento ma dovrebbe dare almeno l&#8217;infarinatura necessaria a comprendere per quale motivo li useremo e come li useremo. Comunque, come ho scritto sopra, consiglio lo stesso la lettura di altri tutorial più approfonditi se avete difficoltà a comprendere quello che dico sugli algoritmi genetici. Avrei tralasciato volentieri questa parte, perchè penso che molti di voi già la conoscano se hanno letto almeno qualche altro documento sull&#8217;argomento reti neurali. Purtroppo mi tocca scriverla lo stesso visto che è un requisito fondamentale per la<br />
comprensione delle parti successive e questo tutorial mira ad essere il più chiaro possibile anche per chi è agli inizi. Comunque, se conoscete già questi argomenti potete tranquillamente saltare tutta la Parte generale del tutorial e passare a leggere direttamente il resto.<br />
<!--adsense--></p>
<p style="text-align: center;"><strong>Come funziona un neurone biologico.</strong></p>
<p style="text-align: justify;">I neuroni sono le cellule che costituiscono la nostra mente e che trasmettono i segnali nervosi (sia tra di loro che verso le altre parti del corpo). Ogni uomo posside circa 100 miliardi di neuroni (o almeno dovrebbe) variamente collegati tra di loro attraverso una struttura che prende il nome di sinapsi.<br />
Ogni neurone possiede un lungo prolungamento chiamato assone e diverse ramificazioni più piccole che prendono il nome di dendriti. Attraverso i dendriti (che sono collegati agli assoni di altri neuroni) la cellula riceve i segnali che gli vengono inviati, mentre attraverso l&#8217;assone viene mandato un nuovo segnale.<br />
Se non avete ben chiaro cio&#8217; che abbiamo detto fino ad ora, l&#8217;immagine seguente dovrebbe chiarvi le idee:</p>
<div class="wp-caption alignnone" style="width: 410px"><img title="Schema di un neurone biologico" src="http://mindunpacked.com/images/neuron.bmp" alt="Schema di un neurone biologico" width="400" height="216" /><p class="wp-caption-text">Schema di un neurone biologico</p></div>
<p style="text-align: justify;">(Nella figura sono presenti parti del neurone che non ho nominato, come per esempio la guaina mielinica, perche&#8217; non sono utili al nostro scopo).<br />
In generale, quando un neurone riceve un segnale, se esso supera una certa soglia (detta soglia di attivazione), propaga quel segnale attraverso il suo assone agli altri neuroni, altrimenti resta inattivo.</p>
<p style="text-align: center"><strong>Come funziona una rete neurale.</strong></p>
<p style="text-align: justify;">Un <strong>neurone artificiale</strong> tenta di emulare il comportamento di quello biologico, anche se in maniera molto semplificata.<br />
Ogni neurone (d&#8217;ora in poi con il termine neurone mi riferirò esclusivamente a quelli artificiali) ha una serie di collegamenti pesati in ingresso e  fornisce un output in base agli input ricevuti dai collegamenti. Per collegamento pesato si intende un collegamento al quale è anche associato un peso,<br />
che rappresenta, in poche parole, la forza del collegamento e quindi quanto esso potrà influenzare l&#8217;output del neurone.<br />
Il peso non è altro che un numero, generalmente compreso tra 0 e 1 (o tra 1e 1).<br />
L&#8217;informazione trasportata dal collegamento (parliamo sempre di un numero) viene moltiplicata per il peso e quindi varia al variare del peso (resta invariata quando esso è uguale a 1, diventa nulla quando è 0 e diventa l&#8217;opposto quando è 1).<br />
Come vedremo, sarà proprio il valore dei pesi a determinare l&#8217;esattezza dei risultati della nostra rete ed è appunto variando i pesi tramite speciali algoritmi che si tenta di raggiungere una configurazione ottimale. Ricordo che i pesi subito dopo l&#8217;inizializzazione della rete hanno valori casuali.<br />
Il neurone, dopo aver ricevuto gli input pesati da tutti i collegamenti in ingresso li somma. La sommatoria degli input pesati viene poi passata ad una funzione chiamata funzione di attivazione che fa propagare il segnale ai neuroni successivi.<br />
Spesso, e questo comportamento differisce da quello del neurone biologico, non viene stabilita una soglia minima di attivazione al di sotto della quale il neurone non invia nessuno stimolo, ma questo dipende dalla funzione di attivazione scelta e lo vedremo più tardi.<br />
Le reti che andremo ad implementare in questo tutorial sono definite <strong>reti feedforward</strong> perchè l&#8217;impulso si propaga sempre nella stessa direzione; esistono molti altri tipi di reti e ognuna è più adatta ad alcuni compiti particolari, ma non ne parleremo in questo tutorial.<br />
Una rete neurale è formata appunto da un certo numero di neuroni, chiaramente molto minore di quello del nostro cervello: in genere alcune decine di neuroni sono in grado di compiere compiti anche piuttosto complessi.<br />
I neuroni di una rete sono organizzati in diversi livelli, o strati, e ognuno di essi ha un compito preciso. Il primo strato è quello di input: contiene i neuroni che ricevono l&#8217;input e non hanno collegamenti pesati; non essendo infatti i dati che ricevono provenienti da nessun altro neurone essi immagazzinano semplicemente il dato così come è e lo trasmettono al livello successivo.<br />
Lo strato di input può essere collegato direttamente a quello di output oppure tra di essi si può trovare un certo numero di strati nascosti (<strong>hidden layers</strong>): per gli scopi di questo tutorial useremo una rete con un solo strato nascosto il quale è più che sufficiente per svolgere i compiti che ci interessano.</p>
<p style="text-align: justify;">Abbiamo visto la struttura di una rete neurale, ma come fa essa ad apprendere? Va detto che esistono due tipi di <strong>apprendimento</strong>: quello <strong>supervisionato</strong> (che tratteremo in questo tutorial) e quello non supervisionato.</p>
<p style="text-align: justify;">Usando il primo tipo di apprendimento la rete necessita di una prima fase di addestramento durante la quale le vengono forniti alcuni esempi comprendenti sia l&#8217;input che l&#8217;output desiderato. La rete viene eseguita con questi input di cui si conosce già in precedenza il risultato e tramite alcuni algoritmi vengono modificati i pesi della rete per fornire risultati sempre più accurati. Quando la fase di allenamento è finita (cioè si e&#8217; raggiunta una soglia minima di errore stabilita in precedenza) la rete è in grando non solo di dare un risultato esatto se gli forniamo gli stessi dati che gli<br />
abbiamo fornito nel <strong>training set</strong> (gli esempi) ma anche con dati in input che non ha mai visto. Questo avviene perchè la rete non associa un singolo input all&#8217;output corrispondente ma scopre la relazione che li lega. Potremmo per esempio allenare una <strong>rete neurale</strong> ad imparare a fare la somma di due numeri fornendo un training set del genere:</p>
<table style="text-align: center" border="0">
<tbody>
<tr>
<td>Primo input</td>
<td>Secondo input</td>
<td>Output desiderato</td>
</tr>
<tr>
<td>0.1</td>
<td>0.2</td>
<td>0.3</td>
</tr>
<tr>
<td>0.1</td>
<td>0.3</td>
<td>0.4</td>
</tr>
<tr>
<td>0.1</td>
<td>0.4</td>
<td>0.5</td>
</tr>
<tr>
<td>0.1</td>
<td>0.5</td>
<td>0.6</td>
</tr>
<tr>
<td>0.1</td>
<td>0.6</td>
<td>0.7</td>
</tr>
<tr>
<td>0.1</td>
<td>0.7</td>
<td>0.8</td>
</tr>
<tr>
<td>etc&#8230;</td>
<td>etc&#8230;</td>
<td>etc..</td>
</tr>
</tbody>
</table>
<p><!--adsense--></p>
<p style="text-align: justify;">Un training set deve essere il più omogeneo possibile, e deve descrivere una grande varietà di esempi. Un training set in cui il primo input è sempre 0.1 non è per niente buono per ottenere dei risultati soddisfacenti. Più è vasto il training set, inoltre, più sarà alta la <strong>capacità di generalizzazione</strong> della rete, cioè di dare risultati corretti anche quando i dati in input sono per lei sconosciuti, cioè non facevano parte del training set.<br />
Modificando in maniera corretta i pesi della rete essa riconosce la relazione tra gli input e l&#8217;output (e cioè, nel nostro caso, che l&#8217;output deve essere la somma degli input) e dà un risultato corretto. Ricordatevi comunque che l&#8217;applicazione delle reti neurali avviene in campi in cui è più importante la flessibilità e la capacità di generalizzazione della stessa, piuttosto che ottenere risultati precisissimi: quello che voglio dire è che una rete neurale è molto più utile per la sua flessibilità che per la sua precisione, e che per fare la somma di dui numeri è molto meglio usare una calcolatrice. Una rete<br />
addestrata a tale scopo, infatti, dà in genere, risultati approssimati come :<br />
0.5 + 0.5 = 0.99<br />
0.1 + 0.9 = 1.02<br />
o simili.<br />
Chiusa questa piccola parentesi torniamo ai tipi di apprendimento. Il secondo tipo di apprendimento (quello non supervisionato) serve a creare reti<br />
neurali che classificano i dati che gli forniamo in input in diverse categorie (senza fornigli nessun output desiderato, da qui il nome) in base ai loro elementi in comune, ma non verra&#8217; trattato in questo tutorial.<br />
Come avrete capito, la parte più importante per l&#8217;apprendimento della rete neurale è la modifica dei pesi fra le connessioni dei neuroni ed è proprio qui che agiscono gli <strong>algoritmi di apprendimento</strong>. Esistono diversi algoritmi: uno dei piu&#8217; famosi è quello di retro propagazione dell&#8217;errore (error back propagation) che mi limiterò a citare solamente visto che noi faremo uso di algoritmi genetici per fare evolvere la nostra rete.</p>
<p>La prima parte del tutorial finisce qui. A presto.</p>
]]></content:encoded>
			<wfw:commentRss>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-i/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- www.000webhost.com Analytics Code -->
<script type="text/javascript" src="http://analytics.hosting24.com/count.php"></script>
<noscript><a href="http://www.hosting24.com/"><img src="http://analytics.hosting24.com/count.php" alt="web hosting" /></a></noscript>
<!-- End Of Analytics Code -->
