@@ -588,7 +588,6 @@ def __init__(
588588 super ().__init__ ()
589589
590590 self .input_dims = input_dims
591- self .predict_affinity = affinity
592591
593592 features : OrderedDict [str , nn .Module ] = OrderedDict (
594593 [
@@ -742,8 +741,6 @@ def forward(self, x):
742741 # Global max pooling reduced spatial dimensions to single value
743742 x = x .view (- 1 , self .features_out_size )
744743
745- pose_raw = self .pose (x )
746-
747744 pose_raw = self .pose (x )
748745 pose_log = F .log_softmax (pose_raw , dim = 1 )
749746
@@ -787,19 +784,16 @@ def __init__(
787784 super ().__init__ (input_dims , num_blocks , num_block_features , num_block_convs )
788785
789786 # Linear layer for binding affinity prediction
790- if self .predict_affinity :
791- self .affinity = nn .Sequential (
792- OrderedDict (
793- [
794- (
795- "affinity_output" ,
796- nn .Linear (
797- in_features = self .features_out_size , out_features = 1
798- ),
799- )
800- ]
801- )
787+ self .affinity = nn .Sequential (
788+ OrderedDict (
789+ [
790+ (
791+ "affinity_output" ,
792+ nn .Linear (in_features = self .features_out_size , out_features = 1 ),
793+ )
794+ ]
802795 )
796+ )
803797
804798 # Xavier initialization for convolutional and linear layers
805799 for m in self .modules ():
@@ -824,6 +818,275 @@ def forward(self, x):
824818 x = x .view (- 1 , self .features_out_size )
825819
826820 pose_raw = self .pose (x )
821+ pose_log = F .log_softmax (pose_raw , dim = 1 )
822+
823+ affinity = self .affinity (x )
824+ # Squeeze last (dummy) dimension of affinity prediction
825+ # This allows to match the shape (batch_size,) of the target tensor
826+ return pose_log , affinity .squeeze (- 1 )
827+
828+
829+ class HiResPose (nn .Module ):
830+ """
831+ GNINA HiResPose model architecture.
832+
833+ Parameters
834+ ----------
835+ input_dims: tuple
836+ Model input dimensions (channels, depth, height, width)
837+
838+ Notes
839+ -----
840+ This architecture was translated from the following Caffe model:
841+
842+ https://github.com/gnina/models/blob/master/crossdocked_paper/hires_pose.model
843+
844+ The main difference is that the PyTorch implementation resurns the log softmax.
845+
846+ This model is implemented only for multi-task pose and affinity prediction.
847+ """
848+
849+ def __init__ (self , input_dims : Tuple ):
850+
851+ super ().__init__ ()
852+
853+ self .input_dims = input_dims
854+
855+ self .features = nn .Sequential (
856+ OrderedDict (
857+ [
858+ # unit1
859+ (
860+ "unit1_conv" ,
861+ nn .Conv3d (
862+ in_channels = input_dims [0 ],
863+ out_channels = 32 ,
864+ kernel_size = 3 ,
865+ stride = 1 ,
866+ padding = 1 ,
867+ ),
868+ ),
869+ ("unit1_func" , nn .ReLU ()),
870+ # unit2
871+ ("unit2_pool" , nn .MaxPool3d (kernel_size = 2 , stride = 2 )),
872+ (
873+ "unit2_conv" ,
874+ nn .Conv3d (
875+ in_channels = 32 ,
876+ out_channels = 64 ,
877+ kernel_size = 3 ,
878+ stride = 1 ,
879+ padding = 1 ,
880+ ),
881+ ),
882+ ("unit2_func" , nn .ReLU ()),
883+ # unit3
884+ ("unit3_pool" , nn .MaxPool3d (kernel_size = 2 , stride = 2 )),
885+ (
886+ "unit3_conv" ,
887+ nn .Conv3d (
888+ in_channels = 64 ,
889+ out_channels = 128 ,
890+ kernel_size = 3 ,
891+ stride = 1 ,
892+ padding = 1 ,
893+ ),
894+ ),
895+ ("unit3_func" , nn .ReLU ()),
896+ ]
897+ )
898+ )
899+
900+ # Two MaxPool3d layers with kernel_size=2 and stride=2
901+ # Spatial dimensions are halved at each pooling step
902+ self .features_out_size = (
903+ input_dims [1 ] // 4 * input_dims [2 ] // 4 * input_dims [3 ] // 4 * 128
904+ )
905+
906+ # Linear layer for pose prediction
907+ self .pose = nn .Sequential (
908+ OrderedDict (
909+ [
910+ (
911+ "pose_output" ,
912+ nn .Linear (in_features = self .features_out_size , out_features = 2 ),
913+ )
914+ ]
915+ )
916+ )
917+
918+ # Linear layer for binding affinity prediction
919+ self .affinity = nn .Sequential (
920+ OrderedDict (
921+ [
922+ (
923+ "affinity_output" ,
924+ nn .Linear (in_features = self .features_out_size , out_features = 1 ),
925+ )
926+ ]
927+ )
928+ )
929+
930+ # Xavier initialization for convolutional and linear layers
931+ for m in self .modules ():
932+ if isinstance (m , nn .Conv3d ) or isinstance (m , nn .Linear ):
933+ nn .init .xavier_uniform_ (m .weight .data )
934+
935+ def forward (self , x : torch .Tensor ):
936+ """
937+ Parameters
938+ ----------
939+ x: torch.Tensor
940+ Input tensor
941+
942+ Notes
943+ -----
944+ The pose score is the log softmax of the output of the last linear layer.
945+ """
946+ x = self .features (x )
947+
948+ print ("FEATURES SHAPE:" , x .shape )
949+
950+ # Reshape based on number of channels
951+ # Global max pooling reduced spatial dimensions to single value
952+ x = x .view (- 1 , self .features_out_size )
953+
954+ pose_raw = self .pose (x )
955+ pose_log = F .log_softmax (pose_raw , dim = 1 )
956+
957+ affinity = self .affinity (x )
958+ # Squeeze last (dummy) dimension of affinity prediction
959+ # This allows to match the shape (batch_size,) of the target tensor
960+ return pose_log , affinity .squeeze (- 1 )
961+
962+
963+ class HiResAffinity (nn .Module ):
964+ """
965+ GNINA HiResAffinity model architecture.
966+
967+ Parameters
968+ ----------
969+ input_dims: tuple
970+ Model input dimensions (channels, depth, height, width)
971+
972+ Notes
973+ -----
974+ This architecture was translated from the following Caffe model:
975+
976+ https://github.com/gnina/models/blob/master/crossdocked_paper/hires_pose.model
977+
978+ The main difference is that the PyTorch implementation resurns the log softmax.
979+
980+ This model is implemented only for multi-task pose and affinity prediction.
981+ """
982+
983+ def __init__ (self , input_dims : Tuple ):
984+
985+ super ().__init__ ()
986+
987+ self .input_dims = input_dims
988+
989+ self .features = nn .Sequential (
990+ OrderedDict (
991+ [
992+ # unit1
993+ (
994+ "unit1_conv" ,
995+ nn .Conv3d (
996+ in_channels = input_dims [0 ],
997+ out_channels = 32 ,
998+ kernel_size = 3 ,
999+ stride = 1 ,
1000+ padding = 1 ,
1001+ ),
1002+ ),
1003+ ("unit1_func" , nn .ReLU ()),
1004+ # unit2
1005+ (
1006+ "unit2_conv" ,
1007+ nn .Conv3d (
1008+ in_channels = 32 ,
1009+ out_channels = 64 ,
1010+ kernel_size = 3 ,
1011+ stride = 1 ,
1012+ padding = 1 ,
1013+ ),
1014+ ),
1015+ ("unit2_func" , nn .ReLU ()),
1016+ # unit3
1017+ ("unit3_pool" , nn .AvgPool3d (kernel_size = 8 , stride = 8 )),
1018+ (
1019+ "unit3_conv" ,
1020+ nn .Conv3d (
1021+ in_channels = 64 ,
1022+ out_channels = 128 ,
1023+ kernel_size = 5 ,
1024+ stride = 1 ,
1025+ padding = 2 ,
1026+ ),
1027+ ),
1028+ ("unit3_func" , nn .ELU (alpha = 1.0 )),
1029+ # unit5 (following original naming convention)
1030+ ("unit5_pool" , nn .MaxPool3d (kernel_size = 4 , stride = 4 )),
1031+ ]
1032+ )
1033+ )
1034+
1035+ self .features_out_size = (
1036+ input_dims [1 ]
1037+ // (8 * 4 )
1038+ * input_dims [2 ]
1039+ // (8 * 4 )
1040+ * input_dims [3 ]
1041+ // (8 * 4 )
1042+ * 128
1043+ )
1044+
1045+ # Linear layer for pose prediction
1046+ self .pose = nn .Sequential (
1047+ OrderedDict (
1048+ [
1049+ (
1050+ "pose_output" ,
1051+ nn .Linear (in_features = self .features_out_size , out_features = 2 ),
1052+ )
1053+ ]
1054+ )
1055+ )
1056+
1057+ # Linear layer for binding affinity prediction
1058+ self .affinity = nn .Sequential (
1059+ OrderedDict (
1060+ [
1061+ (
1062+ "affinity_output" ,
1063+ nn .Linear (in_features = self .features_out_size , out_features = 1 ),
1064+ )
1065+ ]
1066+ )
1067+ )
1068+
1069+ # Xavier initialization for convolutional and linear layers
1070+ for m in self .modules ():
1071+ if isinstance (m , nn .Conv3d ) or isinstance (m , nn .Linear ):
1072+ nn .init .xavier_uniform_ (m .weight .data )
1073+
1074+ def forward (self , x : torch .Tensor ):
1075+ """
1076+ Parameters
1077+ ----------
1078+ x: torch.Tensor
1079+ Input tensor
1080+
1081+ Notes
1082+ -----
1083+ The pose score is the log softmax of the output of the last linear layer.
1084+ """
1085+ x = self .features (x )
1086+
1087+ # Reshape based on number of channels
1088+ # Global max pooling reduced spatial dimensions to single value
1089+ x = x .view (- 1 , self .features_out_size )
8271090
8281091 pose_raw = self .pose (x )
8291092 pose_log = F .log_softmax (pose_raw , dim = 1 )
@@ -841,4 +1104,6 @@ def forward(self, x):
8411104 ("default2018" , True ): Default2018Affinity ,
8421105 ("dense" , False ): DensePose ,
8431106 ("dense" , True ): DenseAffinity ,
1107+ ("hires_pose" , True ): HiResPose ,
1108+ ("hires_affinity" , True ): HiResAffinity ,
8441109}
0 commit comments