Setup dell’Ambiente — CMake, Eigen e OpenCV
Prima di scrivere una sola riga del nostro framework, dobbiamo costruire il cantiere. In questo laboratorio configuriamo i tre strumenti che useremo per tutto il corso:
.cpp in un eseguibile, in modo automatico e indipendente dal sistema operativo.Al termine del laboratorio avrete un progetto che compila e stampa il risultato di un prodotto matrice-vettore.
Per compilare C++17 vi serve un compilatore moderno. Scegliete il vostro sistema:
Linux — tutto già disponibile:
macOS — il compilatore è il clang++ di Apple, dagli strumenti da riga di comando di Xcode; il resto arriva con Homebrew:
Non installate gcc da Homebrew: CMake userebbe comunque clang++, e forzando g++ il collegamento con l’OpenCV di Homebrew (compilata con clang++) può fallire.
Windows — il consiglio è WSL 2 (Windows Subsystem for Linux): un terminale Linux reale dentro Windows, senza macchine virtuali:
Poi seguite le istruzioni Linux. Per verificare:
Per chi usa Windows con WSL 2:
~/...), non in /mnt/c/...: dai dischi di Windows la lettura dei file è molto più lenta, e il CSV di MNIST, che leggerete più avanti, è grande.code .Il consiglio è Visual Studio Code — gratuito, multipiattaforma, estensibile.
Estensioni da installare subito dal pannello Extensions:
Con CMake Tools i pulsanti Build e Run nella barra di stato (o F7 per compilare) sostituiscono i comandi da terminale: come usarli, subito dopo il CMakeLists.txt del corso. Usate comunque il terminale nelle prime settimane per capire cosa succede sotto.
Il progetto è una cartella con due file; build/ la crea CMake:
basic_neural_plusplus/
├── CMakeLists.txt # le istruzioni per CMake
├── main.cpp
└── build/ # creata da cmake -B build: si può cancellare e rigenerare
Il CMakeLists.txt minimo:
Un bersaglio (target) è ciò che CMake costruisce. Le librerie gli si collegano con target_link_libraries, dopo add_executable: lo faremo nelle prossime due slide.
Eigen è header-only: non occorre compilarla. Scegliete il metodo più comodo:
Via package manager (consigliato):
e nel CMakeLists.txt, dopo add_executable:
Manualmente (funziona su tutti i sistemi): scaricate Eigen da eigen.tuxfamily.org e copiate la cartella Eigen/ dentro libs/eigen/ nel vostro progetto; nel CMakeLists.txt, dopo add_executable:
Con QUIET, se non trova Eigen, find_package tace; se la trova imposta Eigen3_FOUND, che il CMakeLists.txt del corso (fra due slide) usa per scegliere fra i due casi.
OpenCV è una libreria compilata: va installata e poi collegata al progetto.
Nel CMakeLists.txt aggiungete, come per Eigen dopo add_executable:
Per verificare che sia installata:
La verifica che conta però è un’altra: quando lancerete cmake -B build, il CMakeLists.txt della prossima slide stamperà da solo la versione che ha trovato. Ubuntu 24.04 ha la 4.6, Homebrew la 5: il codice del corso compila con entrambe.
Ecco il file completo: le righe delle tre slide precedenti, nell’ordine giusto. Lo useremo e aggiorneremo nel corso delle settimane:
cmake_minimum_required(VERSION 3.15)
project(BasicNeuralPlusPlus)
set(CMAKE_CXX_STANDARD 17)
1set(CMAKE_CXX_STANDARD_REQUIRED ON)
2set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
find_package(Eigen3 QUIET)
find_package(OpenCV REQUIRED)
3message(STATUS "OpenCV ${OpenCV_VERSION} trovata")
add_executable(basic_neural main.cpp)
4if(Eigen3_FOUND)
target_link_libraries(basic_neural Eigen3::Eigen)
else()
message(STATUS "Eigen non trovata: uso libs/eigen")
target_include_directories(basic_neural PRIVATE libs/eigen)
endif()
5target_link_libraries(basic_neural ${OpenCV_LIBS})-Wall accende gli avvisi del compilatore, e non è pignoleria: senza, alcuni errori veri restano invisibili. Ne incontrerete due, entrambi verificati — al Laboratorio 03 una ricorsione infinita che compila senza una parola, al Laboratorio 08 uno switch incompleto che azzera tutti i pesi in silenzio.
cmake -B build.
Eigen3::Eigen, il target che il pacchetto di Eigen definisce. Senza questa riga la compilazione fallisce con fatal error: Eigen/Dense: No such file or directory anche con Eigen regolarmente installata. Se invece Eigen manca davvero, ve lo dice già cmake -B build: “Eigen non trovata”.
${OpenCV_INCLUDE_DIRS}.
CMakeLists.txt: CMake Tools la riconosce e propone di configurarla (è il cmake -B build).[Unspecified] lascia scegliere a CMake, come da terminale.basic_neural.build/: test.png finisce in build/test.png, non accanto a main.cpp (da terminale, invece, nella cartella da cui lanciate il programma).#include <Eigen/Dense> in rosso, ma si compila? È IntelliSense che non conosce ancora CMake: accettate quando CMake Tools propone di configurarlo.code .; estensioni con Install in WSL.Useremo questi tipi per tutto il corso:
| Tipo | Cosa rappresenta | Esempio |
|---|---|---|
Eigen::MatrixXf |
Matrice di float, dimensioni dinamiche |
Pesi di uno strato |
Eigen::VectorXf |
Vettore colonna di float |
Bias, uscita di uno strato |
Eigen::ArrayXf |
Array 1D per operazioni elemento per elemento | Funzioni di attivazione |
Xf sta per dimensioni dinamiche (X), elementi float. Per una rete neurale la precisione del float basta, e occupa metà memoria del double. Ci sono varianti double (Xd) e a dimensioni fisse (Matrix3f, ecc.) che non useremo.
#include <Eigen/Dense>
int main() {
Eigen::MatrixXf W = Eigen::MatrixXf::Random(3, 4); // 3x4, valori in [-1,1]
Eigen::VectorXf x = Eigen::VectorXf::Random(4); // 4x1
// Prodotto matrice-vettore → risultato 3x1
Eigen::VectorXf z = W * x;
// Trasposta
1 Eigen::MatrixXf W_trasposta = W.transpose(); // diventa 4x3
// Inizializzazioni utili
Eigen::MatrixXf Z = Eigen::MatrixXf::Zero(3, 4);
Eigen::MatrixXf I = Eigen::MatrixXf::Identity(4, 4);
// Operazioni ELEMENTO PER ELEMENTO con .array(): servono per le attivazioni!
Eigen::ArrayXf relu = z.array().max(0.0f); // ReLU: max(0, zi)
Eigen::ArrayXf sig = 1.0f / (1.0f + (-z.array()).exp()); // Sigmoid
}Wt: fra qualche settimana Wt sarà il nome della matrice dei pesi di un layer, non della trasposta.
Attenzione: * tra due MatrixXf è sempre prodotto matriciale. Per il prodotto elemento per elemento usate .array().
In C stampavate con printf. In C++ si usa lo stream std::cout (header <iostream>), che sa stampare anche le matrici di Eigen:
#include <iostream>
#include <Eigen/Dense>
int main() {
int n = 3; float t = 0.5f;
std::cout << "n = " << n << ", t = " << t << "\n"; // come printf("n = %d, t = %g\n", n, t)
Eigen::VectorXf z = Eigen::VectorXf::Random(3);
std::cout << "z =\n" << z << "\n"; // tutto il vettore
std::cout << z.rows() << "x" << z.cols() << "\n"; // le dimensioni: 3x1
}<< accoda i pezzi da stampare, e il tipo di ciascuno lo riconosce il compilatore: niente %d o %g.printf funziona ancora (header <cstdio>), ma non sa stampare un oggetto di Eigen: servirebbe un ciclo sugli elementi z(i).std:: indica che cout appartiene alla libreria standard; il perché del prefisso lo vedremo a lezione.Verificate che tutto funzioni scrivendo un main.cpp che:
W (\(3 \times 4\), valori casuali) e un vettore x (\(4 \times 1\)).std::cout: le dimensioni devono essere \(3 \times 1\).z_relu = z.array().max(0.0f) e lo stampa: è il vostro primo forward pass (senza bias)!#include <iostream>
#include <Eigen/Dense>
#include <opencv2/opencv.hpp>
int main() {
// punti 1-3: W, x, z = W * x e z_relu, stampati con std::cout
cv::Mat img(28, 28, CV_8UC1, cv::Scalar(128)); // punto 4: grigio medio
cv::imshow("Test MNIST size", img);
cv::waitKey(0); // un tasto, a finestra attiva (chiusa col mouse? Ctrl-C)
}Avviso
cv::imshow apre una finestra grafica. In WSL le finestre funzionano grazie a WSLg, che c’è su Windows 11 e su Windows 10 dalla build 19044 (21H2), purché WSL sia quello installato da wsl --install (la versione dello Store): il comando wsl --version in PowerShell risponde solo in quel caso, e wsl --update lo aggiorna. Collegati via ssh, invece, la finestra non si apre e il programma si ferma con un errore. In caso di problemi, sostituite imshow/waitKey con cv::imwrite("test.png", img); e aprite il file: il resto del corso non dipende dalle finestre.
Su macOS il primo avvio dopo l’installazione di OpenCV può richiedere decine di secondi: il programma non è bloccato, aspettate. Dal secondo avvio è immediato.
Se Eigen stampa e OpenCV apre la finestra (o scrive il PNG), l’ambiente è pronto per tutto il corso.
La soluzione di riferimento verrà discussa all’inizio del prossimo laboratorio.