giovedì 6 febbraio 2025

Bayesian linear regression

Volevo provare un approccio differente dal solito per la regressione lineare

Il problema e' relativo alla terza fase del creep in frane la cui legge tra il tempo e l'inverso della velocita' e' approssimata a lineare

Questi dati sono ripresi dall' articolo Tommaso Carlà,Emanuele Intrieri,Federico Di Traglia,Teresa Nolesini,Giovanni Gigli,Nicola Casagli "Guidelines on the use of inverse velocity method as a tool for setting alarm thresholds and forecasting landslides and structure collapses" Landslides DOI 10.1007/s10346-016-0731-5 per la frana di Montebeni. La freccia verde indica la data dell'evento reale e la freccia rossa la data stimata


 La terza fase e' stata modellata tramite regressione a minimi quadrati applicando alcuni filtri

Ho provato a trattare gli stessi dati tramite regressione lineare bayesiana con Metropolis-Hastings Sampler

Alla fine i risultati sono molto simili. C'e' da notare che 

1) essendo il modello Montecarlo non deterministico i risultati non sono mai identici tra una run e la successiva. Ciao' comporta anche uno spostamento dell'intercetta della retta con l'asse del tempo spostando il TOF (time of failure)

2) sia la regressione a minimi quadrati che quella bayesiana sono molto sensibili al dataset (anche perche' alla fine ci sono solo una vendita di misure). Basta omettere un solo dato all'inizio che la correlazione e' sensibilmente differente. Questo comporta la difficolta' di definire in modo univoco il passaggio dalla fase 2 alla fase 3 del creep  

I dati sono

Tempo,1/V
14, 0.4586046511627907
28, 0.44000000000000006
42, 0.30604651162790697
56, 0.27813953488372095
70, 0.28930232558139535
84, 0.3227906976744187
98, 0.21860465116279076
112, 0.17023255813953395
126, 0.17953488372092963
140, 0.1367441860465109
154, 0.13302325581395344
168, 0.11441860465116252
182, 0.08837209302325501
196, 0.0958139534883719
210, 0.14046511627906993
224, 0.13860465116279058
238, 0.09581395348837188
252, 0.04744186046511625
266, 0.051162790697674376
280, 0.02883720930232564
294, 0.030697674418604465
308, 0.010232558139534276


Per il codice sottostante ho modificato un esempio preso da chatgpt

import sys

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt


# Load CSV data
def load_data(csv_path):
data = pd.read_csv(csv_path)
return data


# Define the likelihood function (Gaussian likelihood)
def likelihood(y, X, beta):
predictions = np.dot(X, beta)
return -0.5 * np.sum((y - predictions) ** 2)


# Define the prior function (Normal prior)
def prior(beta, sigma=10):
return -0.5 * np.sum(beta ** 2) / (sigma ** 2)


# Define the proposal distribution (random walk)
def proposal(beta, step_size=0.1):
return beta + np.random.normal(0, step_size, size=beta.shape)


# Metropolis-Hastings sampler
def metropolis_hastings(X, y, initial_beta, n_samples, step_size=0.1):
beta = initial_beta
samples = []

for _ in range(n_samples):
# Propose new beta
beta_new = proposal(beta, step_size)

# Compute likelihood and prior for current and proposed beta
current_likelihood = likelihood(y, X, beta) + prior(beta)
new_likelihood = likelihood(y, X, beta_new) + prior(beta_new)

# Metropolis-Hastings acceptance step
acceptance_ratio = min(1, np.exp(new_likelihood - current_likelihood))

# Accept or reject the proposed beta
if np.random.rand() < acceptance_ratio:
beta = beta_new

samples.append(beta)

return np.array(samples)


# Prediction function based on posterior samples
def predict(X, posterior_samples):
predictions = np.dot(X, posterior_samples.T)
return predictions.mean(axis=1), predictions.std(axis=1)


def predictLeastSquare(X, beta):
# Add intercept term to the new data
X_with_intercept = np.hstack([np.ones((X.shape[0], 1)), X])
return X_with_intercept @ beta

# Forecast function for new input data
def forecast(X_new, posterior_samples):
# X_new is the new data for which we want to forecast
return predict(X_new, posterior_samples)


def linear_least_squares(X, y):
# Add intercept term (column of ones)
X_with_intercept = np.hstack([np.ones((X.shape[0], 1)), X])

# Compute the coefficients using the normal equation: β = (X^T X)^(-1) X^T y
beta = np.linalg.inv(X_with_intercept.T @ X_with_intercept) @ X_with_intercept.T @ y
return beta

# Main function
def run_bayesian_linear_regression(csv_path, n_samples=1000000, step_size=0.1):
# Load data from CSV
data = load_data(csv_path)

# Assuming the last column is the target 'y' and the rest are features 'X'
X = data.iloc[:, :-1].values
y = data.iloc[:, -1].values

beta = linear_least_squares(X, y)
y_pred = predictLeastSquare(X, beta)

# Add a column of ones for the intercept term (beta_0)
X = np.hstack([np.ones((X.shape[0], 1)), X])

# Initialize the parameters (beta_0, beta_1, ...)
initial_beta = np.zeros(X.shape[1])

# Perform Metropolis-Hastings sampling
posterior_samples = metropolis_hastings(X, y, initial_beta, n_samples, step_size)

# Get predictions
mean_predictions, std_predictions = predict(X, posterior_samples)


# Plot results
plt.figure(figsize=(10, 6))
plt.plot(y, label="True values")
plt.plot(mean_predictions, label="Bayesian Regr")
plt.xlabel("Time")
plt.ylabel("1/V")
plt.fill_between(range(len(y)), mean_predictions - std_predictions,
mean_predictions + std_predictions, alpha=0.3, label="Err")
plt.plot(y_pred, label="Least Square", color='red')

plt.legend()
plt.show()

return posterior_samples


# Usage example
csv_path = 'dati.csv' # Replace with the path to your CSV file
posterior_samples = run_bayesian_linear_regression(csv_path)


new_data = pd.DataFrame({
'feature1': [30, 35] # Replace with your features
})

X_new = np.hstack([np.ones((new_data.shape[0], 1)), new_data.values])

# Forecast values using the posterior samples
mean_forecast, std_forecast = forecast(X_new, posterior_samples)

# Output the forecasts
print("Forecasted values (mean):", mean_forecast)
print("Forecasted values (95% CI):", mean_forecast - 1.96 * std_forecast, mean_forecast + 1.96 * std_forecast)

venerdì 17 gennaio 2025

Pandas su serie tempo

Problema: hai un csv che riporta una serie tempo datetime/valore di un sensore

Effettuare calcoli, ordina le righe, ricampiona il passo temporale, colma i buchi della serie tempo con interpolazione in modo semplice

Data;Valore_p
2024-07-24 12:42:05;0
2024-07-24 12:44:05;0
2024-07-24 12:46:05;0

 soluzione : usa Pandas di Python

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import argrelextrema


#legge i dati dal csv
df_b = pd.read_csv('battente.csv', sep=';',header=0, parse_dates = ['Data'], date_format = '%Y-%m-%d %H:%M:%S')
#ricampiona i dati con un dato al giorno usando il valore medio
df_b_res = df_b.resample('1h', on='Data').mean()
#df_b.iloc[:,1] = df_b.iloc[:,1].apply(lambda x: (x-x.mean())/ x.std())
print(df_b)



df_p = pd.read_csv('pluviometro.csv', sep=';',header=0, parse_dates = ['Data'], date_format = '%Y-%m-%d %H:%M:%S')
#df_p = df_p.shift(periods=3)
df_p_res = df_p.resample('1h', on='Data').mean()

#unisce i due file usando come chiave la colonna temporale
merged_dataframe = pd.merge_asof(df_p_res, df_b_res, on="Data")
#rimuove le righe in cui non ci sono valori
df_finale = merged_dataframe.dropna(how='any')


#cerca i massimi locali facendo scorrere una finestra di 12 ore
df_finale['local_max_p'] = df_finale.iloc[argrelextrema(df_finale.Valore_p.values, np.greater_equal,order=12)[0]]['Valore_p']
df_finale['local_max_b'] = df_finale.iloc[argrelextrema(df_finale.Valore_b.values, np.greater_equal,order=12)[0]]['Valore_b']

#elimina i massimi locali che sono sotto ad una soglia
df_finale['local_max_p'] = np.where(df_finale["local_max_p"] < 1.0,np.nan,df_finale["local_max_p"])
df_finale['local_max_b'] = np.where(df_finale["local_max_b"] < 1.0,np.nan,df_finale["local_max_b"])

df_finale.to_csv("allineato.csv", sep=";")


fig, ax1 = plt.subplots()
# Plot first time series on the primary axis
ax1.plot(df_finale['Data'], df_finale['Valore_b'], 'g-')
ax1.plot(df_finale['Data'], df_finale['local_max_b'], 'mo')

ax1.set_xlabel('Data')
ax1.set_ylabel('Battente (m)', color='g')
ax2 = ax1.twinx()
ax2.plot(df_finale['Data'], df_finale['Valore_p'], 'b-')
ax2.plot(df_finale['Data'], df_finale['local_max_p'], 'ro')

ax2.set_ylabel('Prec.(mm)', color='b')
plt.savefig('grafico.png')
plt.show()






martedì 14 gennaio 2025

Cheshire Cat Ai

Cheshire Ai e' un progetto italiano che sta crescendo adesso


Per provarlo si clona il progetto 

git  clone https://github.com/cheshire-cat-ai/local-cat.git

su un x86 si puo' usare il file compose.yml per avere una installazione di Ollama, Cheshire AI e QDrant

Se non si ha una GPU NVidia si devono commentare le ultime righe

#    deploy:
#      resources:
#        reservations:
#          devices:
#            - driver: nvidia
#              count: all
#              capabilities: [ gpu ]

 

Su Mac invece conviene installare la app nativa di Ollama per M1 in modo da sfruttare la accelerazione Metal e si usa il file docker-compose-mac.yaml che crea un docker di Cheshire AI e QDrant

Per creare su x86 si usa

docker compose up -d

e poi si installano sul docker i modelli (in questo caso mistral)

docker exec ollama_cat ollama pull mistral:7b-instruct-q2_K

Per interagire con Chershire si punta a 


http://localhost:1865/admin/

https://localhost:1865/public

Si deve configurare il language model in Cheshire


altrimenti nel caso di Apple, dove il server Ollama e' esterno al docker si usa l'indirizzo http://host.docker.internal:11434



Usando questo esempio https://cheshirecat.ai/local-embedder-with-fastembed/ si configura l'embedder usando quello interno in modo da girare tutto in locale


Per addestrare il modello tramite RAG e' sufficiente trascinare in PDF sull'interfaccia ed attendere il processamento




Llama.cpp Linux ed Apple

Su Linux la cosa piu' comoda e' scaricare i sorgenti da https://github.com/ggerganov/llama.cpp e compilando


mkdir build 

cd build
make
sudo make install
sudo ldconfig

Su Mac si puo' semplicemente brew install llama.cpp

a questo punto si devono scaricare i modelli in formato GGUF come per esempio

https://huggingface.co/bullerwins/Meta-Llama-3.1-8B-Instruct-GGUF

che pesa circa 4.47 Gb (per confronto gli altri modelli pesanno 2b = 3G 16b = 15Gb)

altrimenti si puo' usare il modello 3.2 (3.42 Gb)

https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF?show_file_info=Llama-3.2-3B-Instruct-Q8_0.gguf

I file GGUF devono copiati nel folder models

Per interagire 

llama-cli -m models/Meta-Llama-3.1-8B-Instruct-Q4_K_S.gguf -p "You are a helpful assistant" --conversation 

llama-cli -m ./models/Llama-3.2-3B-Instruct-Q8_0.gguf -p "You are a helpful assistant" --conversation

Altrimenti per attivare LLama.cpp e scaricare in automatico il modello si usa

llama-cli \                

  --hf-repo "bullerwins/Meta-Llama-3.1-8B-Instruct-GGUF" \

  --hf-file Meta-Llama-3.1-8B-Instruct-Q2_K.gguf \

  -p "You are a helpful assistant" \

  --conversation

Oltre alla modalita' di shell puo' essere attivata la modalita' server 

Usando Homebrew su Mac si puo'usare 

llama-server \

  --hf-repo "bullerwins/Meta-Llama-3.1-8B-Instruct-GGUF" \

  --hf-file Meta-Llama-3.1-8B-Instruct-Q2_K.gguf 



Si punta quindi su http://localhost:8080



venerdì 3 gennaio 2025

Change Detection with structural similarity

L'idea di base e' quella di cercare le differenze tra le due immagini sottostanti

Non e' immediatamente visibile ma ci sono dei sassi che si sono spostati nella zona a coordinata 2600,1600 circa. Per questa prova e' stato impiegato l'algoritmo di Structural Similarity  di SkImage

Foto 1

 

Foto2
il calcolo indica un indice di somiglianza di circa il 71%

 



 Questa e' l'elaborazione (bianco minima differenza)

 


 

Il risultato finale e' la vegetazione con il suo movimento ha completamente obliterato il segnale relativo allo spostamento dei sassi

from skimage.metrics import structural_similarity
import cv2
import numpy as np

# Load images
before = cv2.imread('20241114.jpg')
after = cv2.imread('20241115.jpg')

# Convert images to grayscale
before_gray = cv2.cvtColor(before, cv2.COLOR_BGR2GRAY)
after_gray = cv2.cvtColor(after, cv2.COLOR_BGR2GRAY)

# Compute SSIM between the two images
(score, diff) = structural_similarity(before_gray, after_gray, full=True)
print("Image Similarity: {:.4f}%".format(score * 100))

# The diff image contains the actual image differences between the two images
# and is represented as a floating point data type in the range [0,1]
# so we must convert the array to 8-bit unsigned integers in the range
# [0,255] before we can use it with OpenCV
diff = (diff * 255).astype("uint8")
diff_box = cv2.merge([diff, diff, diff])

# Threshold the difference image, followed by finding contours to
# obtain the regions of the two input images that differ
thresh = cv2.threshold(diff, 20, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]

mask = np.zeros(before.shape, dtype='uint8')
filled_after = after.copy()

for c in contours:
area = cv2.contourArea(c)
if area > 40:
x,y,w,h = cv2.boundingRect(c)
cv2.rectangle(before, (x, y), (x + w, y + h), (36,255,12), 2)
cv2.rectangle(after, (x, y), (x + w, y + h), (36,255,12), 2)
cv2.rectangle(diff_box, (x, y), (x + w, y + h), (36,255,12), 2)
cv2.drawContours(mask, [c], 0, (255,255,255), -1)
cv2.drawContours(filled_after, [c], 0, (0,255,0), -1)

cv2.imshow('20241114', before)
cv2.imshow('20241115', after)
cv2.imshow('diff', diff)
cv2.imshow('diff_box', diff_box)
cv2.imshow('mask', mask)
cv2.imshow('filled after', filled_after)
cv2.waitKey()

 

giovedì 2 gennaio 2025

LLama download checkpoint

Oltre a scaricare i dati per usare un modello puo' essere necessario effettuare un retrain di un modello ed in questo caso non si possono usare i modelli quantizzati ma i dati relativi ad checkpoint


 

per per fare cio' si usa llama-stack

Si deve pero' prima richiesta su  https://www.llama.com/llama-downloads/

e si ottiene una con un link

si installa poi llama-stack

pip install llama-stack
llama model list

llama download --source meta --model-id meta-llama/Llama-3.2-3B 

viene richiesto a questo punto di incollare il link giusto per mail

i file del checkpoint si trovano in .llama

luca@Dell:~$ cd .llama/
luca@Dell:~/.llama$ ls
checkpoints
luca@Dell:~/.llama$ cd checkpoints/
luca@Dell:~/.llama/checkpoints$ ls -la
total 12
drwxr-xr-x 3 luca luca 4096 Dec 31 06:24 .
drwxr-xr-x 3 luca luca 4096 Dec 30 17:30 ..
drwxr-xr-x 2 luca luca 4096 Dec 30 17:31 Llama3.2-3B
luca@Dell:~/.llama/checkpoints$ cd Llama3.2-3B/
luca@Dell:~/.llama/checkpoints/Llama3.2-3B$ ls
checklist.chk  consolidated.00.pth  params.json  tokenizer.model
luca@Dell:~/.llama/checkpoints/Llama3.2-3B$ ls -la
total 6277140
drwxr-xr-x 2 luca luca       4096 Dec 30 17:31 .
drwxr-xr-x 3 luca luca       4096 Dec 31 06:24 ..
-rw-r--r-- 1 luca luca        156 Dec 30 17:31 checklist.chk
-rw-r--r-- 1 luca luca 6425581594 Dec 30 17:36 consolidated.00.pth
-rw-r--r-- 1 luca luca        220 Dec 30 17:31 params.json
-rw-r--r-- 1 luca luca    2183982 Dec 30 17:31 tokenizer.model
luca@Dell:~/.llama/checkpoints/Llama3.2-3B$


Casio SA-38

Ho trovato vicino ad un cassonetto una tastiera SA-38 molto simile a quello che usavo da ragazzo...era in condizioni pessime, molto sporca piu' che altro perche' la gommapiuma della custodia con il tempo si e' polverizzata (nel senso reale della parola) ed e' entrata in ogni pertugio

Troppo curioso di vedere come e' fatta dentro

Aprendo alcuni supporti di plastica delle viti si sono distrutti (non so se qualcuno prima di me la aveva aperta)...inoltre era chiara una perdita di acido nel vano batterie e la corrosione aveva distaccato il cavo del positivo

Dal punto di vista dell'elettronica ci sono veramente pochi componenti ..e' praticamente un SOC (system on a chip) con un AN8053 che funge da regolatore di tensione ed amplificato

AN8053 con jack output audio ed VIN 7.5 V

 

Un MSM6387 in cui sono inseriti tutti i suoni in PCM, un ADC e la gestione degli input dai tasti. La CPU genera una onda a gradini che viene poi smussata da un filtro esterno


MSM6387-11


Su una daughter board piuttosto posticcia c'e' il cristallo oscillatore a 21.725 MHz


I dati di fabbrica indicano una fabbrica in Agosto 1992



tutto e' montato su due schede con connettori saldati (non e' stata pensata per essere smontata e riparata)



i pulsanti sono del tipo presenti in alcune vecchie calcolatrici o tastiere PC con una membrana di gomma che racchiude al suo interno la parte conduttiva


i tasti vengono riconosciuti da una matrice



Stima dell'errore di Linear Bayesian Regression con PyMC

 Sempre seguendo i post precedenti questa e' la stima degli errori relativi ai tre parametri usando NUTS (No-U-Turn Sampler) is an advan...