The Randomized SVD is perhaps the most widely known RandNLA algorithm, having been popularized in Halko et al., 2011. This is perhaps to its simplicity and outstanding practical effectiveness.
Note that the output of Algorithm 5.1 can be easily factored into an “SVD-like” form (where and have orthonormal columns and is non-negative and diagonal) by computing the SVD and then setting .
The approximation is not necessarily of rank-. In most cases this is not an issue, but if a rank- approximation is required, one can simply truncate the approximate SVD to the first singular values; i.e. output .
def randomized_SVD(A,k,b,rank_exactly_k=False):
n,d = A.shape
Ω = np.random.randn(d,b)
Y = A@Ω
Q,_ = np.linalg.qr(Y,mode='reduced')
X = A.T@Q
Up,s,Vt = np.linalg.svd(X.T,full_matrices=False)
if rank_exactly_k:
return Q@Up[:,:k],s[:k],Vt[:k]
else:
return Q@Up,s,Vt
Numerical Example¶
Let’s load an image as a numpy array. Here the image is of dimension 1333 by 2000.
Source
raw_image = plt.imread('nyc.jpg')
A = np.mean(raw_image,axis=-1) # get greyscale image
n,d = A.shape
plt.imshow(A,cmap='gray')
plt.axis('off')
plt.tight_layout()

Compression via SVD¶
We can use the SVD to compress the image. When , we see that the resulting image is essentially indistinguishable from the original image. However, by storing the compressed image in factored form, we require just parameters, which is roughly of the parameters required to store the original image!
Source
start = time.time()
U,s,Vt = np.linalg.svd(A,full_matrices=False)
end = time.time()
SVD_time = end-start
k = 100 # target rank
plt.imshow(U[:,:k]@np.diag(s[:k])@Vt[:k],cmap='gray')
plt.axis('off')
plt.tight_layout()

Randomized SVD¶
Now let’s consider the Randomized SVD. We’ll use a block size , and truncate back to rank-. Similar to the exact SVD, the rank-100 approximation produces by the Randomized SVD looks the same as the original image.
Source
start = time.time()
U_rsvd,s_rsvd,Vt_rsvd = randomized_SVD(A,k,2*k,rank_exactly_k=True)
end = time.time()
RSVD_time = end-start
plt.imshow(U_rsvd@np.diag(s_rsvd)@Vt_rsvd,cmap='gray')
plt.axis('off')
plt.tight_layout()

Runtime comparison¶
The upshot is that the Randomized SVD is way faster!! In this case roughly 20 times faster.
Source
timing = [{
'method':'SVD',
'time (s)': SVD_time,
'speedup': 1
},
{
'method':'Randomized SVD',
'time (s)': RSVD_time,
'speedup': SVD_time/RSVD_time
}]
pd.DataFrame(timing).reindex(columns=['method','time (s)','speedup']).style.format({
'time (s)': '{:.4f}',
'speedup': '{:.1f}x',
})
Theoretical bounds¶
A prototypical bound for the Randomized SVD is the following:
Bounds for the spectral norm are also available.
Full analyses can be found in Halko et al., 2011Tropp & Webber, 2023, but getting some intuition is pretty simple! Write the SVD of as
where , and correspond to the top- singular vectors of ; i.e. .
Observe that
In the case that , then we will obtain so that . In this case, would be the orthogonal projector onto the top left singular vectors of , and we would recover the best rank- approximation. However, when is nonzero, then the we get is perturbed from this ideal case. However, for matrices with fast singular-value decay, will be small, and the impact won’t be so big. Similarly, by increasing , we can make smaller.
- Halko, N., Martinsson, P. G., & Tropp, J. A. (2011). Finding Structure with Randomness: Probabilistic Algorithms for Constructing Approximate Matrix Decompositions. SIAM Review, 53(2), 217–288. 10.1137/090771806
- Tropp, J. A., & Webber, R. J. (2023). Randomized algorithms for low-rank matrix approximation: Design, analysis, and applications. https://arxiv.org/abs/2306.12418