diff --git a/ex-7/plots/fisher.py b/ex-7/plots/fisher.py new file mode 100644 index 0000000..528dc25 --- /dev/null +++ b/ex-7/plots/fisher.py @@ -0,0 +1,117 @@ +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.transforms as trans +import io + + +def line(ax, x, y, **args): + '''line between two points x,y''' + ax.plot([x[0], y[0]], [x[1], y[1]], **args) + + +def angle(v): + '''angle of a vector wrt x axis''' + return np.arctan2(v[1], v[0]) + + +def proj_hist(a, b, w): + '''returns a file-like object of the w-projection histogram''' + fig = plt.figure(figsize=(3, 3)) + ax = fig.add_subplot(111) + ax.set_aspect(0.04) + ax.axis('off') + plt.hist(a @ w, color='xkcd:dark yellow', zorder=2) + plt.hist(b @ w, color='xkcd:pale purple', zorder=1) + buf = io.BytesIO() + fig.savefig(buf, transparent=True) + plt.close(fig) + buf.seek(0) + return plt.imread(buf) + + +def place_im(ax, im, transform): + '''place an image into an Axes by applying a transformation''' + im = ax.imshow(im, interpolation='none', + extent=[-2, 4, -3, 2], + zorder=2) + trans_data = transform + ax.transData + im.set_transform(trans_data) + + +def main(): + # rotation by π/2 matrix + rot = np.array([[0, -1], [1, 0]]) + + # covariance + cov = np.array([[0.33, 0.15], [0.15, 0.12]]) + + # means + m1 = np.array([-1.2, 0.5]) + m2 = np.array([0.6, 0]) + + np.random.seed(3) + a = np.random.multivariate_normal(m1, cov, 100) + b = np.random.multivariate_normal(m2, cov, 100) + + # w₁: naive projection onto m1-m2 plane + w1 = (m1 - m2) / np.linalg.norm(m1 - m2) + + # w₂: Fisher projection + w2 = np.linalg.inv(cov) @ (m1 - m2) + w2 /= np.linalg.norm(w2) + + #plt.rcParams['font.size'] = 8 + fig, axes = plt.subplots( + 1, 2, figsize=(6, 9), + subplot_kw=dict(aspect='equal')) + + # naive projection + ax = axes[0] + ax.set_title('naïve projection', loc='right') + ax.set_xlim(-5.0, 3.5) + ax.set_ylim(-3.5, 2.0) + + ax.scatter(*a.T, edgecolor='xkcd:slate grey', c='xkcd:dark yellow') + ax.scatter(*b.T, edgecolor='xkcd:slate grey', c='xkcd:pale purple') + + dir = rot @ w1 + line(ax, m1, m2, c='xkcd:charcoal') + line(ax, (m1 + m2)/2, (m1 + m2)/2 + 2.2*dir, c='xkcd:charcoal') + line(ax, dir*2.1 - 2.0*w1, dir*2.1 + 2.6*w1, c='xkcd:charcoal') + place_im( + ax, + proj_hist(a, b, w1), + trans.Affine2D() + .rotate(angle(w1)) + .translate(*dir*3.113) + .translate(*w1*(-0.7))) + + # fisher projection + ax = axes[1] + ax.set_title('Fisher projection', loc='right') + ax.set_xlim(-5.0, 3.5) + ax.set_ylim(-3.5, 2.0) + + ax.scatter(*a.T, edgecolor='xkcd:slate grey', c='xkcd:dark yellow') + ax.scatter(*b.T, edgecolor='xkcd:slate grey', c='xkcd:pale purple') + + dir = rot @ w2 + line(ax, m1, m2, c='xkcd:charcoal') + line(ax, (m1 + m2)/2, (m1 + m2)/2 + 2.75*dir, c='xkcd:charcoal') + line(ax, dir*2.9 - 1.2*w2, dir*2.9 + 2.3*w2, c='xkcd:charcoal') + place_im( + ax, + proj_hist(a, b, w2), + trans.Affine2D() + .rotate(angle(w2)) + .scale(0.75) + .translate(*dir*3.90) + .translate(*w2*(-0.3))) + + plt.tight_layout() + #plt.savefig('notes/images/7-fisher.pdf', bbox_inches='tight') + plt.show() + + +if __name__ == '__main__': + main() diff --git a/notes/images/7-fisher.pdf b/notes/images/7-fisher.pdf new file mode 100644 index 0000000..b30e84d Binary files /dev/null and b/notes/images/7-fisher.pdf differ diff --git a/notes/images/7-fisher.png b/notes/images/7-fisher.png deleted file mode 100755 index 1ed0006..0000000 Binary files a/notes/images/7-fisher.png and /dev/null differ diff --git a/notes/sections/7.md b/notes/sections/7.md index 26aed2d..847b0f0 100644 --- a/notes/sections/7.md +++ b/notes/sections/7.md @@ -104,7 +104,7 @@ histograms resulting from the projection onto the line joining the class means: note the considerable overlap in the projected space. The right plot shows the corresponding projection based on the Fisher linear discriminant, showing the greatly improved classes -separation. Figure taken from [@bishop06]](images/7-fisher.png){#fig:overlap} +separation.](images/7-fisher.pdf){#fig:overlap} The overlap of the projections can be reduced by maximising a function that gives, besides a large separation, small variance within each class. The