<?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; Informatica</title>
	<atom:link href="http://mindunpacked.com/category/informatica/feed/?paged=2" 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>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><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">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>2</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>TWM: Tab Window Manager</title>
		<link>http://mindunpacked.com/2008/twm-tab-window-manager/</link>
		<comments>http://mindunpacked.com/2008/twm-tab-window-manager/#comments</comments>
		<pubDate>Tue, 02 Dec 2008 15:28:52 +0000</pubDate>
		<dc:creator>Marco</dc:creator>
				<category><![CDATA[Informatica]]></category>
		<category><![CDATA[configurazione]]></category>
		<category><![CDATA[TWM]]></category>
		<category><![CDATA[window manager]]></category>

		<guid isPermaLink="false">http://mindunpacked.com/?p=190</guid>
		<description><![CDATA[Le interfacce grafiche straripanti di effetti grafici abbondano, e tra Windows Vista e Beryl i cicli macchina ormai si sprecano come il cibo. Fortunatamente c&#8217;è ancora gente che dopo aver esclamato “wow” si rende conto che il sistema è sì più attraente ma l&#8217;usabilità e la velocità sono dimezzate.
In questo articolo parlerò del noto – [...]]]></description>
			<content:encoded><![CDATA[<p style="margin-bottom: 0cm; text-align: justify;">Le interfacce grafiche straripanti di effetti grafici abbondano, e tra Windows Vista e Beryl i cicli macchina ormai si sprecano come il cibo. Fortunatamente c&#8217;è ancora gente che dopo aver esclamato “wow” si rende conto che il sistema è sì più attraente ma l&#8217;usabilità e la velocità sono dimezzate.<br />
In questo articolo parlerò del noto – anche se non sono sicuro che sia poi così noto &#8211; <strong>window manager TWM</strong> (Tab Window Manager), in cui nutro parecchia simpatia. <span id="more-190"></span><br />
Ancora ai tempi in cui Xfree86 era a codice aperto questo window manager veniva distribuito assieme ad esso, ed ora che c&#8217;è <strong>X.Org</strong> la situazione non è cambiata. Lo trovate praticamente in tutte le distribuzioni linux che supportano X11, e probabilmente è il wm più veloce e leggero per sistemi unix. È molto semplice da configurare e può diventare estremamente produttivo facendo solo un po&#8217; di pratica.</p>
<p><center><div id="attachment_191" class="wp-caption aligncenter" style="width: 310px"><a href="http://mindunpacked.com/wp-content/uploads/2008/12/twm.gif"><img class="size-medium wp-image-191" title="twm" src="http://mindunpacked.com/wp-content/uploads/2008/12/twm-300x240.gif" alt="TWM - xterm ed emacs" width="300" height="240" /></a><p class="wp-caption-text">TWM - xterm ed emacs</p></div></center></p>
<p style="margin-bottom: 0cm;">Vediamo innanzitutto come avviarlo. Ovviamente do per scontato che abbiate già configurato il server X e vi funzioni tutto alla perfezione. Prendete il vostro file <strong>.xinitrc</strong> e modificatelo aggiungendo in una nuova riga:</p>
<p style="margin-bottom: 0cm;"><strong>twm</strong></p>
<p style="margin-bottom: 0cm; text-align: justify;">verificate che non siano segnati altri window manager onde evitare conflitti, quindi salvate il file.Prima di avviare la grafica è opportuno copiare il file di configurazione di TWM nella vostra home, così da avere una personalizzazione solo per il vostro utente. Il file si trova solitamente in <strong>/etc/X11/system.twmrc</strong>. Dovete copiarlo nella vostra home rinominandolo semplicemente .twmrc. È anche possibile rinominarlo <strong>.twmrc.&lt;nome_display&gt;</strong> per avere un file di configurazione diverso per ogni display X.<br />
<!--adsense--><br />
Prima di avviarlo, tuttavia,  è meglio verificare che nel menù di TWM ci sia un collegamento quantomeno ad un terminale, come xterm, altrimenti non potreste farci nulla:</p>
<pre class="brush: java;">
menu &quot;main&quot;
{
&quot;Main Menu&quot;		f.title
&quot;&quot;			f.nop
&quot;XTerm&quot;    f.exec  &quot;xterm &amp;amp;amp;amp;amp;amp;&quot;
&quot;Show Icon Manager&quot;	f.showiconmgr
&quot;Hide Icon Manager&quot;	f.hideiconmgr
&quot;&quot;			f.nop
&quot;Exit&quot;		f.menu &quot;Quit-Verify&quot;
}
</pre>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT">Come vedete si tratta di aggiungere un collegamento al terminale, ma questo solo nel caso che il file di configurazione non sia già stato modificato dalla vostra distribuzione. Molte versioni di linux, infatti, lo configurano in automatico, inserendo numerosi programmi nei sotto menù; tra poco vedremo comunque come aggiungere delle voci manualmente. Fatti gli opportuni cambiamenti al file di configurazione non vi resta che lanciare x:</p>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT"><strong>$ startx</strong></p>
<p style="margin-bottom: 0cm;" lang="it-IT">il caricamento è pressoché immediato, e vi ritroverete davanti ad uno schermo completamente vuoto (sempre che non abbiate deciso di avviare dei programmi tramite .xinitrc. Per far apparire il menù basta cliccare in un punto qualsiasi con il tasto sinistro del mouse.<br />
Passiamo ora ad analizzare il file di configurazione. Sempre da TWM, usando il terminale, aprite il file .twmrc con un qualsiasi editor di testo. Potrete salvare i cambiamenti quando vorrete, e vi basterà riavviare TWM per vederli applicati in pochi secondi.</p>
<pre class="brush: java;">
NoGrabServer
RestartPreviousState
DecorateTransients
TitleFont &quot;-adobe-helvetica-bold-r-normal--*-120-*-*-*-*-*-*&quot;
ResizeFont &quot;-adobe-helvetica-bold-r-normal--*-120-*-*-*-*-*-*&quot;
MenuFont &quot;-adobe-helvetica-bold-r-normal--*-120-*-*-*-*-*-*&quot;
IconFont &quot;-adobe-helvetica-bold-r-normal--*-100-*-*-*-*-*-*&quot;
IconManagerFont &quot;-adobe-helvetica-bold-r-normal--*-100-*-*-*&quot;
#ClientBorderWidth

Color
{
BorderColor &quot;slategrey&quot;
DefaultBackground &quot;rgb:2/a/9&quot;
DefaultForeground &quot;gray85&quot;
TitleBackground &quot;rgb:2/a/9&quot;
TitleForeground &quot;white&quot;
MenuBackground &quot;rgb:2/a/9&quot;
MenuForeground &quot;gray85&quot;
MenuBorderColor &quot;slategrey&quot;
MenuTitleBackground &quot;gray70&quot;
MenuTitleForeground &quot;rgb:2/a/9&quot;
IconBackground &quot;rgb:2/a/9&quot;
IconForeground &quot;gray85&quot;
IconBorderColor &quot;gray85&quot;
IconManagerBackground &quot;azure&quot;
IconManagerForeground &quot;gray70&quot;
}
</pre>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT">In questa prima parte si definiscono i vari font utilizzati dal wm, oltre che i colori delle varie parti. Per quest&#8217;ultimo punto i colori vanno scritti in RGB con notazione esadecimale, ciò significa che se avete un valore di blu pari a 162 dovrete scrivere a2. Da notare che è possibile personalizzare i colori per le diverse finestre:</p>
<pre class="brush: java;">
BorderColor  &quot;red&quot; { &quot;XTerm&quot; &quot;yellow&quot; }
</pre>
<p style="text-align: justify;">in questo caso settiamo il bordo delle finestre rosso, ma quello dell&#8217;xterm giallo. È possibile fare la stessa cosa con qualsiasi variabile.</p>
<p style="text-align: justify;">
<pre class="brush: java;">
#
# Define some useful functions for motion-based actions.
#
MoveDelta 3
Function &quot;move-or-lower&quot; { f.move f.deltastop f.lower }
Function &quot;move-or-raise&quot; { f.move f.deltastop f.raise }
Function &quot;move-or-iconify&quot; { f.move f.deltastop f.iconify }

#
# Set some useful bindings.  Sort of uwm-ish, sort of simple-button-ish
#
Button1 = : root : f.menu &quot;main&quot;
Button2 = : root : f.menu &quot;windowops&quot;

Button1 = m : window|icon : f.function &quot;move-or-lower&quot;
Button2 = m : window|icon : f.iconify
Button3 = m : window|icon : f.function &quot;move-or-raise&quot;

Button1 = : title : f.function &quot;move-or-raise&quot;
Button2 = : title : f.raiselower

Button1 = : icon : f.function &quot;move-or-iconify&quot;
Button2 = : icon : f.iconify

Button1 = : iconmgr : f.iconify
Button2 = : iconmgr : f.iconify
</pre>
<p style="margin-bottom: 0cm; text-align: justify;">Qui vengono prima definite delle funzioni per effettuare le classiche operazioni sulle finestre (sposta, ridimensiona, iconifica etc). Successivamente queste funzioni vengono associate ai diversi tasti del mouse. Anche qui potete cambiare quello che volete, invertire i pulsanti come più vi aggrada (il parametro “root” identifica la scrivania).</p>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT">Veniamo ora alla parte forse più pratica ed utile del file di configurazione, ovvero le strutture dei menù.</p>
<pre class="brush: java;">
menu &quot;main&quot;
{
&quot;Main Menu&quot;		f.title
&quot;&quot;			f.nop
&quot;Show Icon Manager&quot;	f.showiconmgr
&quot;Hide Icon Manager&quot;	f.hideiconmgr
&quot;&quot;			f.nop
&quot;Exit&quot;			f.menu &quot;Quit-Verify&quot;
}

menu &quot;Quit-Verify&quot;
{
&quot;Really quit twm?&quot;	f.title
&quot;No, restart twm&quot;	f.restart
&quot;Yes, really quit&quot;	f.quit
}

menu &quot;windowops&quot;
{
&quot;Window Ops&quot;		f.title
&quot;&quot;			f.nop
&quot;Iconify&quot;		f.iconify
&quot;Resize&quot;		f.resize
&quot;Move&quot;			f.move
&quot;Raise&quot;			f.raise
&quot;Lower&quot;			f.lower
&quot;&quot;			f.nop
&quot;Focus&quot;			f.focus
&quot;Unfocus&quot;		f.unfocus
&quot;&quot;			f.nop
&quot;Delete&quot;		f.delete
&quot;Destroy&quot;		f.destroy
}
</pre>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT">Queste righe sono quelle che probabilmente troverete anche nel vostro file .twmrc, e sono quelle standard che non comprendono nessun collegamento a nessuna applicazione, né dei sotto menù. Per creare una categoria nel main vi basta inserire una riga di questo genere:</p>
<pre class="brush: java;">
&quot;Linux&quot;		f.menu &quot;/Linux&quot;
</pre>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT">In questo modo avrete un primo livello di menù con etichetta “Linux”, a cui potrete aggiungere quante voci vorrete:</p>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT">
<pre class="brush: java;">
menu &quot;/Linux/Aiuto&quot;
{
&quot;APT HOWTO&quot;    f.exec  &quot;/usr/bin/apt-howto &amp;amp;amp;amp;amp;&quot;
}
menu &quot;/Linux/Applicazioni&quot;
{
...
}
</pre>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT">Abbiamo così aggiunto due sotto menù alla voce Linux, precisamente Aiuto ed Applicazioni. Tra le parentesi graffe potete aggiungere qualsiasi tipo di collegamento usando la sintassi della riga 3. In questo modo potete costruirvi una rete anche ampia di categorie con le rispettive applicazioni, ottenendo un menù di tutto rispetto.<br />
Sono presenti molte altre variabili, ma per il loro utilizzo vi consiglio di dare un&#8217;occhiata alla <a href="http://linux.die.net/man/1/twm">pagina man di twm</a>. Vorrei invece concludere descrivendo le opzioni che si possono aggiungere al comando twm:</p>
<p style="margin-bottom: 0cm;" lang="it-IT"><strong>-display &lt;num_display&gt;</strong></p>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT">Vi permette di avviare TWM su un display X a vostra scelta tra quelli precedentemente avviati</p>
<p style="margin-bottom: 0cm;" lang="it-IT"><strong>-f &lt;filename&gt;</strong></p>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT">Potete specificare il nome del file di configurazione che twm dovrà utilizzare. Normalmente controlla il file .twmrc nella vostra home, oppure quello di sistema syestem.twmrc</p>
<p style="margin-bottom: 0cm;" lang="it-IT"><strong>-v</strong></p>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT">Questa è una sorta di “verbose mode”, e farà sì che twm stampi a schermo gli eventuali errori che il server X produce. È utile più che altro per sessioni di debugging.</p>
<p style="margin-bottom: 0cm; text-align: justify;" lang="it-IT">Spero di avervi descritto sufficientemente bene l&#8217;utilizzo di questo window manager da “fanatici”, il quale vi permetterà di immergervi completamente in qualsiasi operazione, privati una volta per tutte da qualsivoglia distrazione frivola.</p>
]]></content:encoded>
			<wfw:commentRss>http://mindunpacked.com/2008/twm-tab-window-manager/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Un nuovo metodo per visualizzare l&#8217;insieme di Mandelbrot: il Buddhabrot.</title>
		<link>http://mindunpacked.com/2008/un-nuovo-metodo-per-visualizzare-linsieme-di-mandelbrot-il-buddhabrot/</link>
		<comments>http://mindunpacked.com/2008/un-nuovo-metodo-per-visualizzare-linsieme-di-mandelbrot-il-buddhabrot/#comments</comments>
		<pubDate>Mon, 01 Dec 2008 23:25:58 +0000</pubDate>
		<dc:creator>Francesco</dc:creator>
				<category><![CDATA[Informatica]]></category>
		<category><![CDATA[Buddhabrot]]></category>
		<category><![CDATA[Frattali]]></category>
		<category><![CDATA[Mandelbrot]]></category>

		<guid isPermaLink="false">http://mindunpacked.com/?p=116</guid>
		<description><![CDATA[Chi si interessa di matematica avrà sicuramente sentito parlare di quelle meravigliose figure che sono i frattali. In questo articolo vi parlerò di un metodo particolare, chiamato Buddhabrot, per visualizzare l&#8217;insieme di Mandelbrot che è uno dei frattali più famosi. Per questo motivo darò per scontati alcuni concetti di base e anche la conoscenza dell&#8217;insieme [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Chi si interessa di matematica avrà sicuramente sentito parlare di quelle meravigliose figure che sono i frattali. In questo articolo vi parlerò di un metodo particolare, chiamato Buddhabrot, per visualizzare l&#8217;<strong>insieme di Mandelbrot</strong> che è uno dei frattali più famosi. Per questo motivo darò per scontati alcuni concetti di base e anche la conoscenza dell&#8217;insieme di Mandelbrot. L&#8217;articolo sarà corredato dal codice di un programma in C++, che è lo stesso che ho usato per renderizzare le immagini presenti in questo articolo.<br />
<span id="more-116"></span><br />
Il <strong>Buddhabrot</strong> è una tecnica speciale inventata da <a title="Pagina del Buddhabrot di Melina Green" href="http://www.superliminal.com/fractals/bbrot/bbrot.htm" target="_blank">Melinda Green</a> che porta a dei risultati davvero spettacolari. Per disegnare l&#8217;insieme di Mandelbrot si analizzano vari punti del piano e si considerano appartenenti all&#8217;insieme quelli che <strong>non divergono</strong> (cioè non tendono ad allontanarsi ad una distanza infinita dall&#8217;origine) quando gli viene applicata ripetutamente la formula Z<sub>n+1</sub> = Z<sub>n</sub><sup>2</sup> + C. Nella formula C è un numero complesso che rappresenta le coordinate del punto che stiamo analizzando, e si parte da Z<sub>0</sub> = 0. La tecnica inventata da Melinda Green, invece, tiene in considerazione non la divergenza o meno di un punto ma la traiettoria descritta dai punti che <strong>divergono</strong> quando gli viene applicata ripetutamente la formula. Tutti i punti del piano descriveranno diverse traiettorie, così ci saranno zone meno trafficate e zone più trafficate, le prime risulteranno più scure e le ultime più luminose. Se, invece di considerare le traiettorie dei punti che divergono considerassimo quelle dei punti che non divergono otterremo il cosidetto Anti-Buddhabrot.</p>
<p style="text-align: justify;">Quando si renderizza l&#8217;insieme di Mandelbrot l&#8217;appartenenza di un punto all&#8217;insieme viene stabilita in base alla divergenza dello stesso. Per testare la divergenza viene applicata la formula su di esso diverse volte e, se il punto non accenna a divergere dopo un determinato numero di iterazioni (detto <strong>bailout</strong>) il punto viene considerato appartenente all&#8217;insieme. Chiaramente questo è scorretto dal punto di vista teorico, perchè si dovrebbe verificare che un punto non diverge quando il bailout tende all&#8217;infinito, ma visto che non è possibile effettuare un numero infinito di iterazioni ci si accontenta di un valore di bailout abbastanza alto che consente una notevole approssimazione. Ah, un punto viene considerato divergente quando la sua distanza dall&#8217;origine diventa maggiore di 2.</p>
<p style="text-align: justify;">Quando il bailout è abbastanza alto i risultati che otterremo saranno più precisi. Anche se il bailout è relativamente trascurabile per l&#8217;insieme di <strong>Mandelbrot</strong> (con valori abbastanza alti non si nota la differenza tra un valore ed un altro a meno che non stiamo zoomando), esso diviene fondamentale per renderizzare il Buddhabrot, ecco due immagini con valori di bailout diversi:</p>
<table style="text-align: justify;" border="0">
<tbody>
<tr>
<td>
<p><div id="attachment_129" class="wp-caption alignnone" style="width: 310px"><a href="http://mindunpacked.com/wp-content/uploads/2008/11/1.jpg"><img class="size-medium wp-image-129" title="Buddhabrot N=20" src="http://mindunpacked.com/wp-content/uploads/2008/11/1-300x300.jpg" alt="Buddhabrot N=20" width="300" height="300" /></a><p class="wp-caption-text">Buddhabrot N=20</p></div></td>
<td>
<p><div id="attachment_131" class="wp-caption alignnone" style="width: 310px"><a href="http://mindunpacked.com/wp-content/uploads/2008/11/2.jpg"><img class="size-medium wp-image-131" title="Buddhabrot N=200" src="http://mindunpacked.com/wp-content/uploads/2008/11/2-300x300.jpg" alt="Buddhabrot N=200" width="300" height="300" /></a><p class="wp-caption-text">Buddhabrot N=200</p></div></td>
</tr>
</tbody>
</table>
<p style="text-align: justify;"><!--adsense--><br />
I parametri che concorrono a determinare la qualità dei nostri rendering sono i seguenti:</p>
<ul style="text-align: justify;">
<li><strong>bailout</strong>: come abbiamo appena detto, più il bailout è elevato e minore sarà il margine di errore che potremmo avere su ogni punto. Per esempio, ci sono punti che divergono molto lentamente e che con un bailout basso vengono considerati non divergenti.</li>
</ul>
<ul style="text-align: justify;">
<li><strong> numero di pixels esaminati</strong>: di più pixels esaminiamo le traiettorie e più nitida sarà la nostra immagine. Quando il numero di pixels non è sufficiente otteniamo un fastidioso &#8220;effetto griglia&#8221;.</li>
</ul>
<ul style="text-align: justify;">
<li><strong> risoluzione</strong>: ovviamente, più è alta la risoluzione è migliore sarà la nostra immagine. C&#8217;è da notare, però, che immagini a risoluzione più alta richiedono un numero di pixels esaminati maggiore per ottenere risultati buoni, rispetto a immagini con risoluzione minore.</li>
</ul>
<p style="text-align: justify;">I tempi di rendering per questo tipo di frattale sono piuttosto lunghi, e quando i parametri qualitativi sono elevati diventano quasi insopportabili.</p>
<p style="text-align: justify;">Le immagini che abbiamo visto fino ad ora sono tutte in bianco e nero. Per ottenere risultati a colori viene usata una tecnica simile a quella che la NASA usa per <a title="Come la NASA colora le immagini" href="http://hubblesite.org/sci.d.tech/behind_the_pictures/meaning_of_color/index.shtml" target="_blank">colorare le immagini astronomiche</a>, cioè i <a title="Falsi colori, Wikipedia" href="http://en.wikipedia.org/wiki/False-color" target="_blank">falsi colori</a>. Così come la NASA assegna un colore diverso ad ogni frequenza della luce che riceve dallo spazio, noi creiamo tre immagini in bianco e nero con diversi valori di bailout e le assegnamo rispettivamente ai canali R, G e B dell&#8217;immagine. Otterremo in questa maniera immagini simili alle seguenti:</p>
<table style="text-align: justify;" border="0">
<tbody>
<tr>
<td>
<p><div id="attachment_181" class="wp-caption alignnone" style="width: 310px"><a href="http://mindunpacked.com/wp-content/uploads/2008/12/3.jpg"><img class="size-medium wp-image-181" title="Buddahbrot 1" src="http://mindunpacked.com/wp-content/uploads/2008/12/3-300x300.jpg" alt="" width="300" height="300" /></a><p class="wp-caption-text">valore di bailout = 500, risoluzione = 1024x1024, tempo di rendering 50 minuti</p></div></td>
<td>
<p><div id="attachment_182" class="wp-caption alignnone" style="width: 310px"><a href="http://mindunpacked.com/wp-content/uploads/2008/12/davebrotmy6.jpg"><img class="size-medium wp-image-182" title="Buddahbrot" src="http://mindunpacked.com/wp-content/uploads/2008/12/davebrotmy6-300x300.jpg" alt="" width="300" height="300" /></a><p class="wp-caption-text">I dati di questa immagine non li ricordo... spero mi perdoniate.</p></div></td>
</tr>
</tbody>
</table>
<p style="text-align: justify;">(Nota: i colori dell&#8217;ultima immagine sono stati leggermente ritoccati con Photoshop)</p>
<p style="text-align: justify;">Bene, per ora è tutto. Spero di scrivere un secondo articolo su questo argomento in cui descriverò un&#8217;altra tecnica per renderizzare animazioni con il Buddhabrot (che in realtà può essere immaginato come un oggetti in 4 dimensioni), ma siccome i tempi di rendering sono piuttosto lunghi, dovrete aspettare che finisca di renderizzare un bel video. Allego qui il file sorgente del programma che ho usato per realizzare i rendering: <a title="Buddhabrot C++" href="http://www.mindunpacked.com/risorse/buddhabrot.tar.gz" target="_self">Buddhabrot C++</a></p>
<p style="text-align: justify;">Alla prossima&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://mindunpacked.com/2008/un-nuovo-metodo-per-visualizzare-linsieme-di-mandelbrot-il-buddhabrot/feed/</wfw:commentRss>
		<slash:comments>5</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>3</slash:comments>
		</item>
		<item>
		<title>Algoritmi steganografici</title>
		<link>http://mindunpacked.com/2008/algoritmi-steganografici/</link>
		<comments>http://mindunpacked.com/2008/algoritmi-steganografici/#comments</comments>
		<pubDate>Mon, 24 Nov 2008 20:13:48 +0000</pubDate>
		<dc:creator>Marco</dc:creator>
				<category><![CDATA[Informatica]]></category>
		<category><![CDATA[permutazione]]></category>
		<category><![CDATA[S-Tools]]></category>
		<category><![CDATA[Steganografia]]></category>
		<category><![CDATA[Texto]]></category>

		<guid isPermaLink="false">http://mindunpacked.com/?p=67</guid>
		<description><![CDATA[Nel precedente articolo avevo parlato dell&#8217;algoritmo LSB, che è il capostipite degli algoritmi steganografici basati sulla sostituzione dei bit ridondanti. In questo articolo descriverò abbastanza dettagliatamente uno degli algoritmi steganografici più utilizzati, ovvero quello di Andrew Brown, fra l&#8217;altro implementato in un software chiamato S-Tools.
Prima di arrivare a ciò, vorrei descrivere come la steganografia possa [...]]]></description>
			<content:encoded><![CDATA[<p style="margin-bottom: 0cm; text-align: justify;">Nel <a href="http://mindunpacked.com/2008/introduzione-alla-steganografia-digitale/" target="_blank">precedente articolo</a> avevo parlato dell&#8217;algoritmo <strong>LSB</strong>, che è il capostipite degli algoritmi steganografici basati sulla sostituzione dei bit ridondanti. In questo articolo descriverò abbastanza dettagliatamente uno degli algoritmi steganografici più utilizzati, ovvero quello di <strong>Andrew Brown</strong>, fra l&#8217;altro implementato in un software chiamato S-Tools.<span id="more-67"></span><br />
Prima di arrivare a ciò, vorrei descrivere come la steganografia possa essere attaccata in modo da vanificarne gli scopi.</p>
<p style="margin-bottom: 0cm; text-align: justify;">In primis va chiarito che colui che tenta di violare una trasmissione di informazioni segreta viene chiamato steganalista, e la sua materia di studio è la steganalisi. Secondo il principio di Kerckhoff, valido egualmente anche in crittografia, la sicurezza della steganografia non fa imputata al fatto che il sistema steganografico sia sconosciuto allo steganalista, ma bensì che la sola chiave per accedere all&#8217;informazione non sia nota.<br />
Gli attacchi si possono raggruppare in due piccoli gruppi, quelli passivi e quelli attivi:</p>
<p style="margin-bottom: 0cm;" align="left"><span style="color: #ff6600;"><strong>Attacchi passivi:</strong></span></p>
<p style="text-align: justify;"><strong>stego-only-attack:</strong> lo stegoanalista intercetta il file steganografato ed è in grado di 	analizzarlo. E&#8217; il più importante tipo di attacco contro un sistema 	steganografico perché è quello che solitamente capita di 	utilizzare.<br />
<strong>stego-attack:</strong> il mittente ha usato lo stesso 	cover ripetutamente per nascondere i dati. Lo steganalista  possiede 	un file steganografato diverso ma originato dallo stesso cover. La 	differenza è che il messaggio celato all&#8217;interno sarà diverso, ma 	il compito di analisi sarà agevolato dal fatto che il file 	contenitore è il medesimo. Per rendere inutile questo tipo di 	attacco, come anche il precedente, basta avere l&#8217;accortezza di 	utilizzare sempre un file contenitore diverso, e soprattutto di 	scarsa notorietà.<br />
<strong>cover-stego-attack:</strong> In questo caso lo 	steganalista intercetta un file steganografato ma è a conoscenza di 	quale file “vergine” è stato utilizzato come cover. Basterà 	fare un confrontro tra i due per identificare i bit modificati, ed 	in caso di assenza di protezioni aggiuntive, risalire 	all&#8217;informazione.<br />
<strong>cover-emb-stego-attack:</strong> Bè, questo è il caso più favorevole, in cui lo stegoanalista possiede tutte le informazioni necessarie per estrapolare l&#8217;informazione. Tuttavia ad successiva sessione i dati potrebbero cambiare, e quindi costringere l&#8217;analista ad un stego-only-attack.</p>
<p><strong><span style="color: #008000;">Attacchi attivi:</span></strong></p>
<p><strong>manipulating the stego data:</strong> L&#8217;analista è in grado di frapporsi tra il mittente ed il destinatario, modificare il file steganografato eliminando l&#8217;informazione occultata e quindi inviare il file modificato al destinatario. Una sorta di attacco man in the middle.<strong><br />
manipulating the cover data:</strong> In questo caso si è in grado di manipolare il cover e intercettare il file steganografato. Si potrà risalire all&#8217;informazione nascosta attraverso passaggi più o meno compessi, magari sempre confrontando i due file cercando di scoprire i bit incriminati.<strong><br />
</strong><br />
<!--adsense--><br />
<span style="color: #999999;"><strong>L&#8217;algoritmo di A. Brown e S-Tools</strong></span></p>
<p style="margin-bottom: 0cm; text-align: justify;">Andrew Brown è l&#8217;artefice di uno degli algoritmi steganografici più utilizzati, che lui stesso ha implementato in un software chiamato S-Tools (scaricabile <a href="ftp://ftp.ntua.gr/pub/crypt/mirrors/idea.sec.dsi.unimi.it/code/s-tools4.zip">qui</a>).</p>
<p style="margin-bottom: 0cm; text-align: justify;">Si tratta di un algoritmo sostitutivo appartenente alla famiglia LSB. E&#8217; in grado di nascondere più messaggi nello stesso file, che in questo caso può essere un&#8217;immagine in formato gif o bitmap oppure un file sonoro con estensione wav. L&#8217;algoritmo procede attraverso tre fasi:</p>
<ul>
<li>
<p style="margin-bottom: 0cm;" align="justify">Compressione dei file da 	nascondere</p>
</li>
<li>
<p style="margin-bottom: 0cm;" align="justify">Cifratura dei dati 	mediante vari algoritmi (<strong>IDEA</strong>, <strong>DES</strong>, <strong>triplo DES</strong> e <strong>MDC</strong>) con password 	scelta dall&#8217;utente</p>
</li>
<li>
<p style="margin-bottom: 0cm;" align="justify">Generazione casuale di 	una serie di numeri che individuano i byte dove inserire il 	messaggio nascosto.</p>
</li>
</ul>
<p style="margin-bottom: 0cm; text-align: justify;">I primi due stadi sono dei preliminari e consentono di pretrattare i dati da nascondere secondo metodi distinti dalla steganografia; essi infatti vengono compressi in modo che occupino meno spazio e quindi criptati per avere un ulteriore grado di sicurezza. Il primo punto consente anche di avere un cover di dimensioni più piccole.</p>
<p style="margin-bottom: 0cm; text-align: justify;">Il terzo passo è il più importante da un punto di vista steganografico. Tale fase si occupa di iniettare il messaggio nascosto nei bit meno significativi di alcuni byte scelti casualmente, o meglio, a seconda della password inserita dall&#8217;utente. Considerando come file di copertura un wav, è noto come quest&#8217;ultimo si ottiene attraverso la simultanea applicazione dei due procedimenti di campionamento e <a href="http://it.wikipedia.org/wiki/Convertitore_analogico-digitale" target="_blank">quantizzazione</a>. Per esempio in Windows la quantizzazione è attuata con 8 o 16 bit, cosicché si può attingere rispettivamente a scale di 256 e <strong>65536</strong> valori. Cambiare il bit meno significativo di alcuni byte che operano la digitalizzazione, soprattutto nel secondo caso, produce modificazioni impercettibili anche alla più sensibile delle orecchie. Si stratta quindi di un processo del tutto analogo a quello adottato per le immagini. La particolarità dell&#8217;algoritmo di Brown sta nel fatto che i byte da modificare vengono scelti casualmente e dipendono sempre dalla password immessa dall&#8217;utente.</p>
<p style="margin-bottom: 0cm; text-align: justify;"><span style="color: #ffffff;">.</span></p>
<p style="margin-bottom: 0cm; text-align: justify;"><strong>Steganografia nei testi : Texto</strong></p>
<p style="margin-bottom: 0cm; text-align: justify;">Alcuni algoritmi sono in grado di nascondere dei messaggi all&#8217;interno di frasi opportunamente generate, ed è proprio questo il caso di Texto (sorgenti scaricabili <a href="http://www.nic.funet.fi/pub/crypt/steganography/texto.tar.gz">qui</a>), un programma sviluppato da Kevin Maher. Va detto subito che le frasi prodotte, pur avendo un significato grammaticale, assumono un indiscutibile valore semantico, apparendo il più delle volte come periodi demenziali. Ovviamente le frasi generate da Texto sono in inglese, essendo lo stesso autore di lingua anglosassone. Maher ha approntato un dizionario di parole divise in categorie che sono: oggetti, luoghi, verbi, avverbi e aggettivi. Inoltre ha generato delle frase che fanno da template da completare con le parole appena menzionate.<br />
Ogni categoria è costituita da 64 alternative, quindi si possono codificare fino a 6 bit, ed essendo gli spazi vuoti cinque (per ciascun template), il testo segreto per ogni frase può essere costituito da un massimo di 30 bit. Il tutto funziona se il mittente ed il destinatario utilizzano il medesimo dizionario, inoltre è facile intuire come l&#8217;esigua quantità di parole faciliti il compito dello steganalista. Tuttavia la sicurezza può essere aumentata ampliando il dizionario ed il numero di template, o magari cifrando il testo prima di steganografarlo.</p>
<p style="margin-bottom: 0cm;"><strong><br />
</strong></p>
<p><span style="color: #999999;"><strong> <span>Permutazioni pseudo-casuali</span></strong></span></p>
<p style="margin-bottom: 0cm; text-align: justify;">Gli algoritmi di <strong>permutazione pseudo-casuale</strong> appartengono sempre alla famiglia degli LSB e si basano ugualmente sulla modifica dei bit meno significativi. La differenza sta nella scelta dei byte da modificare, che non viene fatta come una sequenza seppure random come nel caso di <strong>S-Tools</strong>, ma con la permutazione di un sottoinsieme di tali byte. Una permutazione di n oggetti è la nuova disposizione degli stessi secondo un nuovo ordine. Il numero totale di permutazioni aumenta in modo esponenziale all&#8217;aumentare del numero di oggetti – byte in questo caso. In particolare le permutazioni di n byte sono n! (n <a href="http://it.wikipedia.org/wiki/Fattoriale" target="_blank">fattoriale</a>), quindi anche una piccola frase da 512 bit può essere permutata in talmente tanti modi che l&#8217;analizzarli tutti richiederebbe – anche ad un computer – un tempo non indifferente. Scegliendo come successione di byte da modificare una permutazione del sottoinsieme prescelto si perde la caratteristica di ordine, cosicché il messaggio occultato viene “sparpagliato” tra i byte del cover. La distribuzione non ordinata dei bit modificati tra i byte disponibili fornisce un ulteriore elemento di sicurezza contro eventuali attacchi.<br />
Questa è una tecnica utilizzata molto anche in crittografia, ma ovviamente le modalità di impiego sono ben diverse.</p>
<h3 style="margin-bottom: 0cm; text-align: justify;"><span style="color: #ffffff;">.</span></h3>
<p style="margin-bottom: 0cm; text-align: justify;"><strong>Conclusione</strong></p>
<p style="margin-bottom: 0cm; text-align: justify;">Bè, si fa per dire. D&#8217;altronde di cose da esporre sulla steganografia ce ne sarebbero ancora molte, ma diciamo che per il momento è tutto. Per il futuro ho intenzione di illustrarvi come funziona un&#8217;altra interessante applicazione pratica della steganografia, ovvero i file system steganografati. Continuate a seguirci <img src='http://mindunpacked.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://mindunpacked.com/2008/algoritmi-steganografici/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Reti neurali attraverso algoritmi genetici in C++. Parte II</title>
		<link>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-ii/</link>
		<comments>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-ii/#comments</comments>
		<pubDate>Sun, 23 Nov 2008 18:00:53 +0000</pubDate>
		<dc:creator>Francesco</dc:creator>
				<category><![CDATA[Informatica]]></category>
		<category><![CDATA[Algoritmi genetici]]></category>

		<guid isPermaLink="false">http://mindunpacked.com/?p=88</guid>
		<description><![CDATA[Algoritmi genetici.
Gli algoritmi genetici sono algoritmi particolari che si ispirano all&#8217;evoluzione naturale delle specie descritta da Darwin. Essi sono molto utilizzati nel risolvere problemi nei quali lo spazio di ricerca delle soluzioni non è ben definito e garantiscono sempre un avvicinamento alla soluzione ideale del problema. In un algoritmo genetico ogni individuo (cromosoma) possiede un [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: center"><strong>Algoritmi genetici.</strong></p>
<p style="text-align: justify;">Gli algoritmi genetici sono algoritmi particolari che si ispirano all&#8217;evoluzione naturale delle specie descritta da Darwin. Essi sono molto utilizzati nel risolvere problemi nei quali lo spazio di ricerca delle soluzioni non è ben definito e garantiscono sempre un avvicinamento alla soluzione ideale del problema. In un algoritmo genetico ogni individuo (cromosoma) possiede un <strong>DNA</strong> che rappresenta una potenziale soluzione del problema. Una popolazione di un certo numero di individui con DNA casuale viene creata e su di essa agiscono varie operazioni atte a simulare la selezione naturale per scegliere via via quelli che più si avvicinano alla soluzione desiderata. Il DNA di ogni individuo è in genere una soluzione del problema codificata in modo da facilitare le operazioni genetiche che bisogna fare su di essa (crossover, mutazioni genetiche, etc&#8230;, le vedremo fra poco).</p>
<p><span id="more-88"></span></p>
<p style="text-align: justify;">Per fare un esempio, potremmo scegliere di creare un algoritmo genetico che cerchi due numeri interi che diano come somma il numero (intero) passatogli in input. Un esempio di DNA per questo algoritmo potrebbe essere il seguente:</p>
<p style="text-align: left">00010100</p>
<p style="text-align: justify;">Dove le due quartine rappresentano i due numeri da noi cercati codificati in forma binaria. La forma binaria non è l&#8217;unica possibile per codificare il DNA, infatti ognuno potrebbe scegliere di usare un&#8217;altra codifica, ma nel caso appena descritto si rivela abbastanza comoda (anche perchè è di facile decodifica). Ora abbiamo visto come si codifica il DNA, ma come avviene l&#8217;evoluzione? Vediamolo.</p>
<p style="text-align: justify;">All&#8217;inizio viene creata una popolazione di individui con DNA casuale e ogni individuo di questa popolazione viene valutato, cioè gli viene dato un punteggio più o meno alto in base a quanto si è avvicinato alla soluzione che cerchiamo. Qui entra in gioco una delle parti più importanti nell&#8217;algoritmo genetico e cioè la <strong>funzione di fitness</strong>. Il fitness è proprio il punteggio ottenuto da ogni individuo e questa funzione serve a calcolarlo. Nel nostro caso è molto facile creare una funzione di fitness: per assegnare un punteggio basta semplicemente fare la differenza in valore assoluto tra il numero desiderato in output e il numero ottenuto tramite la somma dei due numeri codificati. Supponiamo di volere il numero 12 come output e di avere due cromosomi con il seguente DNA:</p>
<p style="text-align: left">1) 00010010</p>
<p style="text-align: left">2) 01000101</p>
<p style="text-align: justify;">Il primo codifica i numeri 1 e 2 in forma binaria (0001 == 1, 0010 == 2) mentre il secondo i numeri 4 e 5 (0100 == 4, 0101 == 5). Ilcromosoma 1) da come risultato 3 mentre il 2) da come risultato 9. Se calcoliamo il fitness semplicemente facendo la differenza il primo avra&#8217; un fitness di 121= 11, mentre il secondo 129= 3. Chiaramente nel nostro esempio il fitness più basso corrisponde al cromosoma migliore e un fitness pari a 0 corrisponde all&#8217;aver raggiunto la soluzione perfetta. La funzione di fitness in questo caso è molto semplice, ma spesso è un fattore determinante per la buona riuscita di un algoritmo genetico: scegliere una funzione di fitness al posto di un&#8217;altra puo&#8217; cambiare drasticamente i risultati!</p>
<p style="text-align: justify;">Dopo aver calcolato il fitness per tutti gli individui della popolazione, si passa alla riproduzione. La riproduzione avviene emulando la selezione naturale e cioè facendo sopravvivere gli individui più forti (quelli con il fitness migliore) ed eliminando quelli più deboli. Durante la riproduzione avvengono anche i fenomeni del crossover e della mutazione che servono a creare varietà  genetica (altrimenti i cromosomi resterebbero sempre gli stessi) e che vedremo fra un istante.</p>
<p style="text-align: justify;">Gli individui da far riprodurre non posso essere scelti casualmente, perchè altrimenti non si assisterebbe mai ad un miglioramento con il passare delle generazioni (ah, per generazione si intende  ogni ciclo completo di riproduzione), perciò esistono vari metodi di selezione e noi useremo quella che viene chiamata selezione a torneo (tournament selection). Giusto per la cronaca, un altro dei metodi di selezioni molto utlizzati è la “<strong>roulette  wheel selection</strong>” che assegna ad ogni cromosoma una probabilità di essere selezionato direttamente proporzionale al suo fitness. La selezione a torneo consiste nello scegliere casualmente N individui dalla popolazione di partenza e di confrontare il loro fitness: quello con il fitness migliore viene scelto per la riproduzione. La selezione a torneo permette di variare facilmente la pressione selettiva: più è alto il numero N degli individui partecipanti al torneo è più basse saranno le possibilità di riproduzione degli individui con fitness basso (con basso intendo semplicmente peggiore e non minore numericamente, perchè come abbiamo visto, nel nostro caso, un fitness minore equivale ad un punteggio migliore).</p>
<p><!--adsense--></p>
<p style="text-align: justify;">Gli individui con fitness basso, sono comunque tutt&#8217;altro che inutili perchè in seguito alla ricombinazione genetica, possono fornire materiale utile alla ricerca della soluzione (leggete più avanti per averne un esempio). In genere vengono scelti due cromosomi alla volta (e quindi vengono effettuati due &#8220;tornei&#8221;) e da loro nascono due nuovi individui. I figli, se così si possono chiamare, non avranno lo stesso patrimonio genetico dei genitori (altrimenti non assisteremmo mai ad un miglioramento e l&#8217;individuo migliore della prima generazione resterebbe sempre il migliore per tutte le generazioni seguenti) ma avranno un patrimonio che deriva dal loro in seguito ad alcune modifiche. Ad introdurre queste modifiche sono due operatori genetici: il crossover e la mutazione. Chi ha studiato un po&#8217; di biologia dovrebbe conoscere almeno in linea teorica il crossover: esso avviene quando due cromosomi si rompono in un determinato punto e si scambiano una parte di DNA. Supponiamo di avere due cromosomi:</p>
<p style="text-align: left">1) <span style="color: #ff0000;">00100</span> <span style="color: #ff0000;">100</span> ( 2 + 4 = 6 ) fitness = 6</p>
<p style="text-align: left">2) <span style="color: #ff0000;">00011 010</span> ( 1 + 10 = 11 ) fitness = 1</p>
<p style="text-align: left">
<p style="text-align: justify;">e supponiamo di scegliere come punto di rottura quello indicato dalla freccia. Effettuando il crossover nasceranno due figli con i seguenti DNA:</p>
<p style="text-align: left">1a) <span style="color: #ff0000;">00100</span><span style="color: #0000ff;">010 </span> ( 2 + 2 = 4 ) fitness = 8</p>
<p style="text-align: left">2b) <span style="color: #0000ff;">00011</span><span style="color: #ff0000;">100 </span> ( 1 + 12 = 13 )  fitness = 1</p>
<p style="text-align: justify;">che come vedete sono l&#8217;incrocio dei due DNA precedenti. Il crossover è fondamentale perchè puo&#8217; produrre nuovi cromosomi che si avvicinano molto di piu&#8217; alla soluzione: nell&#8217;esempio il cromosoma 2b) dà come risultato 13 che è quasi la soluzione che cercavamo. E&#8217; interessante notare come, in questo caso, un individuo con fitness basso (il cromosoma 1) con fitness 6) abbia contribuito in maniera fondamentale al raggiungimento della soluzione. Generalmente il crossover non avviene sempre, ma si stabilisce una probabilità (il 70% va bene) con cui farlo avvenire. Dopo aver creato i due nuovi individui su di essi agisce un altro operatore genetico, la mutazione, che effettua un cambiamento su un singolo gene del DNA. Se il DNA è  codificato in forma binaria la mutazione non fa altro che trasforamare uno 0 in un 1 o viceversa. Anch&#8217;essa non si verifica in ogni riproduzioni ma ha una probabilità che in genere è molto piu&#8217; bassa rispetto a quella del crossover. Questo procedimento di riproduzione si ripete fino a che la nuova popolazione non ha un numero di individui pari a quella vecchia e poi si ricomincia tutto da capo: si rivaluta il fitness dei nuovi individui (se tutto va bene troveremo qualcuno con un fitness migliore), si effettua una nuova riproduzione, etc&#8230; tutto questo si ripete fino a che non troviamo una soluzione accettabile al nostro problema.</p>
<p style="text-align: justify;">Probabilmente sarete un po&#8217; confusi, e ora vi starete chiedendo cosa c&#8217;entrino gli algoritmi genetici con le reti neurali. Fra un secondo vi sarà tutto più chiaro. Abbiamo detto che la parte fondamentale per ottenere risultati esatti con una rete neurale è trovare una configurazione ottimale dei pesi e sarà proprio per trovare i valori di tutti i pesi della rete che useremo gli algoritmi genetici. Il DNA dei nostri cromosomi rappresenterà i pesi della rete e via via cercheremo di individuare soluzioni migliori facendo riprodurre quei cromosomi il cui DNA fornisce risultati con un errore più basso quando viene eseguito nella rete. In pratica prendiamo un cromosoma e sostituiamo il suo DNA ai pesi della rete e verifichiamo quant&#8217;è l&#8217;errore prodotto: in questo caso la funzione di fitness corrisponde semplicemente all&#8217;errore, ed anche qui cromosomi con fitness più basso (e quindi con errore più basso) saranno migliori. C&#8217;e&#8217; da notare anche che il DNA non verra codificato ma sarà costituito da un semplice vettore di double che contiene tutti i pesi di tutti i neuroni della rete neurale.</p>
<p style="text-align: justify;">La parte sugli algoritmi genetici è terminata e spero che quello che ho scritto sia sufficiente a farvi capire quello che implementeremo. Consiglio ancora una volta a chi non ha ben chiari questi concetti di leggere alcuni dei tuorial sugli algoritmi genetici consigliati in fondo e soprattutto, di provare a realizzare una propria implementazione di un piccolo algoritmo genetico perchè può aiutare molto.</p>
]]></content:encoded>
			<wfw:commentRss>http://mindunpacked.com/2008/reti-neurali-attraverso-algoritmi-genetici-in-c-parte-ii/feed/</wfw:commentRss>
		<slash:comments>5</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>
		<item>
		<title>Introduzione alla steganografia digitale</title>
		<link>http://mindunpacked.com/2008/introduzione-alla-steganografia-digitale/</link>
		<comments>http://mindunpacked.com/2008/introduzione-alla-steganografia-digitale/#comments</comments>
		<pubDate>Wed, 19 Nov 2008 22:41:45 +0000</pubDate>
		<dc:creator>Marco</dc:creator>
				<category><![CDATA[Informatica]]></category>
		<category><![CDATA[LSB]]></category>
		<category><![CDATA[Steganografia]]></category>
		<category><![CDATA[Watermark]]></category>

		<guid isPermaLink="false">http://mindunpacked.com/?p=30</guid>
		<description><![CDATA[Introduzione
La parola steganografia significa letteralmente – in greco &#8211; “scrittura nascosta”, e questo vi potrebbe far pensare subito alla crittografia in quanto anche questa, in un certo senso, ha lo scopo di occultare dei messaggi. Tuttavia fra le due c&#8217;è una differenza sostanziale: mentre la crittografia si preoccupa di rendere un messaggio illeggibile agli occhi [...]]]></description>
			<content:encoded><![CDATA[<p style="margin-bottom: 0cm;"><strong>Introduzione</strong></p>
<p style="text-align: justify;">La parola steganografia significa letteralmente – in greco &#8211; “scrittura nascosta”, e questo vi potrebbe far pensare subito alla crittografia in quanto anche questa, in un certo senso, ha lo scopo di occultare dei messaggi. Tuttavia fra le due c&#8217;è una differenza sostanziale: mentre la crittografia si preoccupa di rendere un messaggio illeggibile agli occhi di una persona non autorizzata, la steganografia fa in modo che una persona non autorizzata non si renda nemmeno conto dell&#8217;esistenza del messaggio in questione, malgrado esso sia totalmente in chiaro. La fusione delle due tecniche, cioè steganografare un messaggio criptato, è un ottima soluzione per aumentare il livello di sicurezza.<span id="more-30"></span></p>
<p style="margin-bottom: 0cm; text-align: justify;">La steganografia è piuttosto antica come tecnica, e solo ultimamente è stata introdotta in campo digitale. Gli stessi geroglifici egiziani possono essere considerati una sorta di messaggi steganografati, in quanto potrebbero essere dei semplici disegni per chi non ne conosce il significato, ma al contrario fonte di molte informazioni per chi sa decifrarli. A parte questo esempio, i fautori di questa tecnica furono proprio i greci che incidevano il testo su delle tavolette per poi ricoprirle di cera, e quindi occultare il testo. Lo sviluppo concreto della tecnica, tuttavia, fu opera di <a href="http://it.wikipedia.org/wiki/Johannes_Trithemius" target="_blank">Johannes Trithemius</a> (pseudonimo di <em><span style="font-style: normal;">Johann</span></em><span style="font-style: normal;"> Heidenberg)</span> che inventò quello che oggi è conosciuto a tutti come <strong>acrostico</strong>, ovvero una frase che si forma mettendo insieme delle parole – in modo più o meno sensato &#8211; le cui iniziali vadano a comporre un ulteriore messaggio. Questa tecnica fu comunque perfezionata da Heidenberg stesso, seguendo alcuni piccoli accorgimenti. Più recentemente, ed in particolare durante la seconda guerra mondiale, i nazisti usavano la tecnica dei micropunti fotografici: sono delle particolari fotografie dalle dimensioni irrisorie che una volta sviluppate ed ingrandite svelano il messaggio nascosto.</p>
<p style="margin-bottom: 0cm;">Questo era un breve excursus storico, ma ora passiamo a cose più tecniche.</p>
<p><!--adsense--></p>
<p><strong>La steganogarfia digitale</strong></p>
<p style="margin-bottom: 0cm; text-align: justify;">In ambito digitale occultare una qualche informazione equivale a sostituire dei bit relativamente poco importanti (chiamati bit ridondanti) con bit che, rimessi insieme con un opportuno algoritmo, formino un&#8217;informazione diversa dal file che li contiene. I dati da nascondere vengono celati all&#8217;interno di contenitori (detti cover o medium), i quali possono essere dei file di diversa natura (immagini, video, testi etc). I metodi utilizzati per fare ciò sono sostanzialmente tre:</p>
<p style="margin-bottom: 0cm; text-align: justify;"><strong>Iniezione</strong>: questo metodo consiste nell&#8217;aggiungere &#8211; quindi qui non si parla di sostituzione &#8211; dei bit all&#8217;interno del cover. La pecca di questa tecnica è che la grandezza del file può aumentare anche considerevolmente, a seconda dei bit iniettati, ma ovviamente per dei piccoli dati può andare bene.</p>
<p style="margin-bottom: 0cm; text-align: justify;"><strong>Sostituzione</strong>: si tratta di sostituire i bit inutili, o meglio ridondanti, presenti nel file, con quelli da noi voluti. A seconda della percentuale di bit sostituiti l&#8217;aspetto del file può variare più o meno considerevolmente. In un&#8217;immagine, ad esempio, alcuni dettagli potrebbero svanire o degradarsi.</p>
<p style="text-align: justify;"><strong>Generazione di un nuovo file</strong>: in questo caso non è necessario nessun cover, ma ne viene creato uno che sia perfettamente adatto ad ospitare i dati da occultare. In questo modo eventuali artefatti non si dovrebbero notare.</p>
<p style="margin-bottom: 0cm;">Vorrei aprire una piccola parentesi riguardo ai <strong>watermark</strong> ed ai <strong>fingerprint</strong>.</p>
<p style="margin-bottom: 0cm; text-align: justify;">I watermark non sono altro che messaggi o semplicemente delle firme che vengono nascoste nelle immagini il più delle volte per identificarne l&#8217;autore, così da ridurre le violazioni del copyright. Molti programmi di grafica professionale includono questa funzione. Anche svariate macchine fotografiche digitali inseriscono nelle foto dati riguardanti l&#8217;esposizione i tempi e quant&#8217;altro, ed il tutto può essere all&#8217;occorrenza letto. Si tratta anche qui di algoritmi steganografici, che però vengono usati per scopi più pratici che per questioni di sicurezza o privacy.</p>
<p style="margin-bottom: 0cm; text-align: justify;">Il fingerprint, invece, è una sorta di watermark, ma applicato principalmente a dei prodotti software per prevenire la copia pirata degli stessi. In pratica per ogni copia venduta viene iniettato un codice univoco in qualche file del software, ed in caso di necessità questo viene usato per verificare l&#8217;autenticità dello stesso, ovvero che non esistano altri software con il medesimo codice.</p>
<p style="margin-bottom: 0cm; text-align: justify;">Ritornando ai suddetti metodi di occultaggio, quello di sostituzione è il più utilizzato, questo perché raggiunge un compromesso tra perdita di informazioni del cover ed informazioni iniettate. Inoltre si può adattare a moltissimi tipi di file.</p>
<p style="margin-bottom: 0cm; text-align: justify;">Per questa introduzione mi limiterò a descrivere uno degli algoritmi di base più diffusi, usato principalmente per occultare informazioni nelle immagini (in questo caso immagini senza compressione, come le bitmap). Si tratta dell&#8217;algoritmo LSB (Least Significant Bit, bit meno significativo) che in parole povere non fa altro che sostituire gli ultimi bit di una sequenza di byte, quindi riducendo al minimo gli <a href="http://it.wikipedia.org/wiki/Artefatto_(segnali)" target="_blank">artefatti</a>. Questo è possibile anche grazie ad alcune nozioni spiegate dalla teoria dell&#8217;informazione e dipende dall&#8217;<a href="http://it.wikipedia.org/wiki/Entropia_(teoria_dell%27informazione)" target="_blank">entropia</a> sempre minore del 100% che contraddistingue la maggior parte dei file non compressi. Comunque non approfondirò questo argomento in questa sede, anche perché andrei abbastanza fuori tema.</p>
<p><strong>L&#8217;algoritmo LSB</strong></p>
<p>Come già detto questo algoritmo non fa altro che cambiare il bit low-end dei vari byte, ad esempio:</p>
<p>1100101(1)</p>
<p style="text-align: justify;">in questo caso l&#8217;ultimo bit (1) è quello che possiamo modificare. Vediamo ora com&#8217;è possibile sostituire dei bit nelle immagini bitmap, o comunque prive di compressione.<br />
Queste immagini sono solitamente in modalità RGB, cioè ad ogni pixel corrisponde un determinato valore di rosso, verde e blu. 255, 255, 255 = pixel bianco 0, 0, 0 = pixel nero. E così per tutti gli altri colori. Ora, se voi provate a colorare un pixel con la sequenza 255, 253, 255 il risultato vi sembra forse diverso dal bianco? A meno che non abbiate una vista da superdotati, la risposta è no.</p>
<p style="margin-bottom: 0cm;">Per ogni pixel quindi sono necessari 3 byte per definirne il colore:</p>
<p style="margin-bottom: 0cm;">1111 1111<br />
1111 1111<br />
1111 1111</p>
<p style="margin-bottom: 0cm; text-align: justify;">in questo caso viene definito il colore bianco, ricordando comunque che il primo byte determina la percentuale di rosso, il secondo di verde, ed il terzo di blu. L&#8217;algoritmo sfrutta proprio questa particolarità della struttura delle immagini per inserire dati in bit che sono sostanzialmente irrilevanti.<br />
Quindi poniamo di voler occultare la lettera “a” (01100001) in una sequenza di byte, quale può essere un&#8217;immagine:<br />
&#8230;<br />
11011010<br />
10001011<br />
11001000<br />
01011010<br />
11001111<br />
11101010<br />
01011010<br />
11001011<br />
&#8230;</p>
<p>ecco come procedere:</p>
<p>&#8230;<br />
11011010<br />
10001011<br />
1100100(1) &lt;&#8211; bit modificato<br />
01011010<br />
1100111(0) &lt;&#8211; bit modificato<br />
11101010<br />
01011010<br />
11001011<br />
&#8230;</p>
<p style="text-align: justify;">Come avete visto è bastato modificare solo due bit perché gli altri corrispondevano già a quelli da nascondere. In questo caso è praticamente impossibile capire che il file è stato steganografato, ma ovviamente può capitare di dover nascondere molti più bit, e quindi mutarne di più. Per esempio anche una semplice frase che in totale occupa 20 byte necessiterà – nel peggiore dei casi &#8211; di una sequenza di 160 byte. Comunque nella quasi totalità dei casi, secondo statistiche, il numero di bit da alterare è circa la metà dei bit da occultare nel file.</p>
<p style="margin-bottom: 0cm; text-align: justify;">L&#8217;LSB sta alla base di molti altri algoritmi di sostituzione che riescono ad iniettare informazioni anche in immagini compresse come le jpeg o le gif, oppure in file audio o video. Tratterò questi algoritmi nello specifico in altri articoli, magari anche citando alcuni programmi che ne fanno uso.</p>
]]></content:encoded>
			<wfw:commentRss>http://mindunpacked.com/2008/introduzione-alla-steganografia-digitale/feed/</wfw:commentRss>
		<slash:comments>3</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 -->
