<jupyter_start><jupyter_text>Multi Layer Neural Network h1 and h2 are two units in hidden layerThe first layer shown on the bottom here are the inputs, understandably called the input layer. The middle layer is called the hidden layer, and the final layer (on the right) is the output layer. We can express this network mathematically with matrices again and use matrix multiplication to get linear combinations for each unit in one operation. For example, the hidden layer ($h_1$ and $h_2$ here) can be calculated$$\vec{h} = [h_1 \, h_2] = \begin{bmatrix}x_1 \, x_2 \cdots \, x_n\end{bmatrix}\cdot \begin{bmatrix}           w_{11} &amp; w_{12} \\           w_{21} &amp;w_{22} \\           \vdots &amp;\vdots \\           w_{n1} &amp;w_{n2}\end{bmatrix}$$The output for this small network is found by treating the hidden layer as inputs for the output unit. The network output is expressed simply$$y =  f_2 \! \left(\, f_1 \! \left(\vec{x} \, \mathbf{W_1}\right) \mathbf{W_2} \right)$$The number of hidden units a parameter of the network, often called a hyperparameter to differentiate it from the weights and biases parameters<jupyter_code>#import pyTorch
import torch
def activation(x):
    return 1/(1+ torch.exp(-x))
#Generate some data
torch.manual_seed(7) #set the random seeds so things are predictible
#Creating feature vector
features = torch.randn((1,3))

#Define size of each layer in the network
n_input = features.shape[1]   # number of input units must match 
n_hidden = 2                  # number of hidden units
n_output = 1                  # number of output units

#Weights from input to hidden layer
w1 = torch.randn(n_input, n_hidden)
#Weights from hidden to output layer
w2 = torch.randn(n_hidden, n_output)

#Bias terms for hidden and output layer
B1 = torch.randn((1,n_hidden))
B2 = torch.randn((1,n_output))

hidden_output = activation(torch.mm(features,w1) + B1)
output = activation(torch.mm(hidden_output ,w2) + B2)
print(output)<jupyter_output>tensor([[0.3171]])<jupyter_text>Numpy to Torch and backSpecial bonus section! PyTorch has a great feature for converting between Numpy arrays and Torch tensors. To create a tensor from a Numpy array, use torch.from_numpy(). To convert a tensor to a Numpy array, use the .numpy() method.<jupyter_code>import numpy as np
a = np.random.rand(4,3)
print(a)
#Convert numpy array to tensor
b = torch.from_numpy(a)
print(b)
b.numpy()<jupyter_output><empty_output><jupyter_text>The memory is shared between the Numpy array and Torch tensor, so if you change the values in-place of one object, the other will change as well.<jupyter_code>#Changing tensor changes numpy array also
b.mul_(2)
print(a)
print(b)<jupyter_output>[[2.910474   3.168238   2.43266519]
 [3.31889972 0.50207659 1.34372172]
 [1.42389461 0.75022299 1.45460107]
 [1.13821941 1.3333499  2.67643385]]
tensor([[2.9105, 3.1682, 2.4327],
        [3.3189, 0.5021, 1.3437],
        [1.4239, 0.7502, 1.4546],
        [1.1382, 1.3333, 2.6764]], dtype=torch.float64)
{'fraction_non_alphanumeric': 0.10572097691535631, 'fraction_numerical': 0.07126129140180662, 'mean_word_length': 4.111111111111111, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '453522', 'license': 'MIT', 'n_tokens_mistral': 1103, 'n_tokens_neox': 981, 'n_words': 355, 'path': '3_Multi Layer Neural Networks & numpy to torch.ipynb', 'repo_name': 'AkankshaShrimal/Deep-Learning-Pytorch-Step-Wise-Learning', 'size': '2989'}