====== Laboratorium 3: Regresja liniowa ======
===== Zbiory danych =====
* {{ :ed:xy-001.csv | xy-001}} $f_{true}=2.37x+7$
* {{ :ed:xy-002.csv |xy-002}} $f_{true}=-1.5x^2+3x+4$
* {{ :ed:xy-003.csv | xy-003}} $f_{true}=-1.5x^2+3x+4$
* {{ :ed:xy-004.csv |xy-004}} $f_{true}=-10x^2+500x-25$
* {{ :ed:xy-005.csv |xy-005}} $f_{true}=(x+4)(x+1)(x-3)$
* {{ :ed:xy-006.csv | xy-006}} box
* {{ :ed:xy-007.csv | xy-007}} circle
* {{ :ed:xy-008.csv |xy-008}} fat-ellipse
* {{ :ed:xy-009.csv | xy-009}} ellipse
* {{ :ed:xy-010.csv | xy-010}} ellipse-outliers
===== 1. Przetwarzamy zbiór xy-001 =====
==== 1.1. Ładowanie i przetwarzanie wstępne ====
**1.** Załaduj zbiór danych i wyświetl zawartość
**2.** Algorytm regresji działa na danych numerycznych. Kolumny ''X'' i ''Y'' są danymi numerycznymi, ale wejściem dla algorytmu jest wektor cech. Dlatego X musi być przekonwertowane do postaci wektora.
Uzyj klasy [[https://spark.apache.org/docs/latest/api/java/org/apache/spark/ml/feature/VectorAssembler.html|VectorAssembler]], a w szczególności metod:
* ''setInputCols()'' - jedyną kolumną do przekonwertowania jest ''X''
* ''setOutputCol("features")'' - po konwersji kolumna z wektorami danych ma się nazywać ''features''
* ''transform()'' do przekonwertowania danych
Oczekiwany wynik:
+--------+---------+----------+
| X| Y| features|
+--------+---------+----------+
|0.581807| 3.930072|[0.581807]|
|0.969903| 6.831824|[0.969903]|
| 1.03564| 6.630985| [1.03564]|
|1.284787| 6.558356|[1.284787]|
|1.949874|15.588728|[1.949874]|
+--------+---------+----------+
only showing top 5 rows
root
|-- X: double (nullable = true)
|-- Y: double (nullable = true)
|-- features: vector (nullable = true)
==== 1.2 Regresja ====
Typowy przebieg procesu uczenia to
* Definiowanie algorytmu i jego parametrów - klasa ''LinearRgression''
* Budowa modelu - klasa ''LinearRegressionModel''
* Ocena modelu (w tym przypadku tylko dla zbioru uczącego na podstawie danych zebranych w obiekcie klasy ''LinearRegressionTrainingSummary'')
**1.** Dodaj poniższy kod
LinearRegression lr = new LinearRegression()
.setMaxIter(10)
.setRegParam(0.3)
.setElasticNetParam(0.8)
.setFeaturesCol("features")
.setLabelCol("Y");
// Fit the model.
LinearRegressionModel lrModel = lr.fit(df_trans);
**2.** Wydrukuj współczynniki regresji za pomocą metod ''+ lrModel.coefficients()'' oraz ''lrModel.intercept()''
**3.** Wyświetl informacje o przebiegu uczenia i metryki
LinearRegressionTrainingSummary trainingSummary = lrModel.summary();
System.out.println("numIterations: " + trainingSummary.totalIterations());
System.out.println("objectiveHistory: " + Vectors.dense(trainingSummary.objectiveHistory()));
trainingSummary.residuals().show(100);
System.out.println("MSE: " + trainingSummary.meanSquaredError());
System.out.println("RMSE: " + trainingSummary.rootMeanSquaredError());
System.out.println("MAE: " + trainingSummary.meanAbsoluteError());
System.out.println("r2: " + trainingSummary.r2());
**4.** Jakie metryki są wyświetlane. Co to są ''residuals''
**5** Wyświetl historię uczenia ''trainingSummary.objectiveHistory()'' za pomocą poniższej funkcji:
static void plotObjectiveHistory(List lossHistory){
var x = IntStream.range(0,lossHistory.size()).mapToDouble(d->d).boxed().toList();
Plot plt = Plot.create();
plt.plot().add(x, lossHistory).label("loss");
plt.xlabel("Iteration");
plt.ylabel("Loss");
plt.title("Loss history");
plt.legend();
try {
plt.show();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (PythonExecutionException e) {
throw new RuntimeException(e);
}
}
==== 1.3 Wykres funkcji i danych ====
Napisz funkcję służącą do rysowania wykresów:
* danych
* przebiegu funkcji regresji
* opcjonalnie: $f_{true}$ - prawdziwej funkcji z której został wygenerowany zbiór danych
/**
*
* @param x - współrzedne x danych
* @param y - współrzedne y danych
* @param lrModel - model regresji
* @param title - tytuł do wyswietlenia (może być null)
* @param f_true - funkcja f_true (może być null)
*/
static void plot(List x, List y, LinearRegressionModel lrModel, String title, Function f_true){
**1.** Rysowanie danych x,y jest proste
Plot plt = Plot.create();
plt.plot().add(x, y,"o").label("data");
**2.** Oblicz zakresy zmienności ''x'' -- zmienne ''xmin'' i ''xmax'' i wygeneruj listę punktów
var xdelta = 0.05*(xmax-xmin);
var fx = NumpyUtils.linspace(xmin-xdelta,xmax+xdelta,100);
**3.** Dla wszystkich wartości w ''fx'':
* przekształć je do postaci jednoelemntowego wektora typu ''DenseVector'' [[https://spark.apache.org/docs/latest/api/java/org/apache/spark/ml/linalg/DenseVector.html]]
* wywołaj ''lrModel.predict()'' z wektorem jako argumentem
* zgromadź wartości na liście ''fy''
* najkrócej - strumieniem i map
Wyświetl:
plt.plot().add(fx, fy).color("r").label("pred");
Jeżeli ''f_true!=null'' oblicz jej wartość dla każdego elementu ''fx'': ''f_true.apply(_x)'' i zgromadź wynik na liście. Następnie wyświetl:
plt.plot().add(fx, fy_true).color("g").linestyle("--").label("$f_{true}$");
oczekiwany wynik:
{{ :ed:linreg-xy-001-0.png?direct&400 |}}
==== 1.4 Potencjalny wpływ parametrów algorytmu ====
Zaimplementowany w Apache Spark algorytm liniowej regresji może:
* Ustawić wagę składników regularyzacji w funkcji straty, lub je pominąć co odpowiada setRegParam(0.0)
* Mieszać składniki regularyzacji L1 i L2 ustwiajac proporcje za pomocą ''setElasticNetParam(alpha)''. Cytując [[https://spark.apache.org/docs/latest/api/java/org/apache/spark/ml/regression/LinearRegression.html#elasticNetParam()|dokumentację]]: //Param for the ElasticNet mixing parameter, in range [0, 1]. For alpha = 0, the penalty is an L2 penalty. For alpha = 1, it is an L1 penalty.//
**1.** Ile iteracji zaobserwowano, jeżeli ustawiono ''setRegParam(0.0)''
**2.** Przetestuj dla wartości parametru regularyzacji 10,20,50,100
* Jak wyglądają wykresy (zamieść rysunki)
* Jak zmieniają się miary, np. współczynnik determinacji r2
===== 2. Przetwarzamy kolejne zbiory =====
Dla kolejnych zbiorów danych:
* Wyświetl wykresy
* Wyznacz metryki i zbierz ich wartości w tabelce
===== 3. Porównanie xy-002 i xy-004 =====
==== 3.1 Wyniki regresji dla xy-002 ====
{{ :ed:linreg-xy-002.png?direct&400 |}}
Użyj następującego kodu w języku Python, aby wyświetlić informacje statystyczne dotyczące wyznaczonych współczynników.
W tym przypadku stosowana jest metoda OLS (ang. //ordinary least squares//, czyli czysta regresja bez regularyzacji $𝑤=[X^TX]^{-1}X^Ty$ ). Metoda wymaga dodania kolumny z jedynkami na początku tablicy z danymi.
import numpy as np
import statsmodels.api as sm
x,y = np.loadtxt('xy-002.csv',delimiter=',',unpack=True,skiprows=1)
X_plus_one = np.stack( (np.ones(x.size),x), axis=-1)
X_plus_one
ols = sm.OLS(y, X_plus_one)
ols_result = ols.fit()
print(ols_result.summary())
OLS Regression Results
==============================================================================
Dep. Variable: y R-squared: 0.933
Model: OLS Adj. R-squared: 0.933
Method: Least Squares F-statistic: 1370.
Date: Sat, 09 Mar 2024 Prob (F-statistic): 2.10e-59
Time: 18:20:11 Log-Likelihood: -711.10
No. Observations: 100 AIC: 1426.
Df Residuals: 98 BIC: 1431.
Df Model: 1
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 652.8037 60.627 10.768 0.000 532.491 773.116
x1 -74.3271 2.008 -37.012 0.000 -78.312 -70.342
==============================================================================
Omnibus: 11.135 Durbin-Watson: 0.062
Prob(Omnibus): 0.004 Jarque-Bera (JB): 7.706
Skew: -0.547 Prob(JB): 0.0212
Kurtosis: 2.191 Cond. No. 61.2
==============================================================================
*Jaką wartość ma współczynnik determinacji?
*Jaki jest błąd standardowy wyznaczonych współczynników?
*W jakim zakresie mieszczą się z 95% wiarygodnością?
==== 3.2 Wyniki regresji dla xy-004 ====
{{ :ed:linreg-xy-004.png?direct&400 |}}
* Wyznacz analogiczne wartości dla ''xy-004''
* Jak zinterpretujesz fakt, że dolna granica przedziału ufności dla współczynnika x1 to liczba ujemna, a górna to dodatnia?
* Narysuj możliwe skrajne przebiegi